Как определить, был ли указатель удален и безопасно удалить его?
В С++ Как определить или узнать, был ли указатель удален до
когда я попытался удалить указатель, который был ранее удален в другой части кода, он выдал исключение, которое невозможно обработать.
Мне было интересно, есть ли способ проверить или попробовать удалить указатель? любая ссылка на операции расширенной памяти.
Кроме того, я хочу оспаривать не обработанные исключения указателей, а доступ к защищенным или доступ является нарушением,... такого рода ошибки.
спасибо тем, кто дает некоторые из своих знаний и времени, чтобы помочь другим людям и поделиться своими преимуществами.
Update
Большой совет от большого количества современного сообщества разработчиков С++ - используйте интеллектуальные указатели или старайтесь избегать использования необработанных указателей. Но для защиты от бросков и обеспечения свободной памяти (ISO_CPP_FAQ), и, конечно, если вы хотите избежать небольших накладных расходов при использовании интеллектуальных указателей [может не быть заметны всегда, но у них есть накладные расходы] вы можете написать свои собственные методы, которые имеют дело с необработанными указателями [type *] - это не общее.
Предпочитают всегда интеллектуальные указатели на исходные указатели.
В "Going Native 2013" общий совет: "Никогда не используйте необработанные указатели".
Ответы
Ответ 1
Могут быть три решения. Вы можете выбрать один в зависимости от соотношения усилий/качества, которое вы хотите достичь:
Элегантное и правильное решение:
Используйте интеллектуальные указатели, и вам больше не нужно вручную вызывать delete
снова. Это лучший способ преодолеть эту проблему. Он использует принцип RAII, который отлично работает для языка, такого как С++, который не имеет встроенного сборщика мусора.
Менее элегантное, но работоспособное решение:
Назначьте указатель на NULL
после удаления. Вызов delete
в указателе NULL
- это нет-op, поэтому он устраняет необходимость иметь дополнительную проверку NULL
, но это может скрыть некоторые проблемы, а не сделать их видимыми.
Менее элегантное, но более правильное решение:
Остановите все множественные проблемы delete
, разрешив вашей программе сбой. Вы могли бы также использовать программы анализатора памяти, такие как valgrind, а затем исправить свой код, чтобы избежать всех этих проблем.
Ответ 2
Это хороший вопрос, но одна из основных правды работы в среде с управляемой вручную памятью (например, C/С++ и ее кузенами) заключается в том, что нет хорошего способа взглянуть на указатель после факта и спросить, valid - как только он станет недействительным, он исчез, и, глядя на него, он склонен к взрыву. Ваша задача состоит в том, чтобы убедиться, что он никогда не удалялся или не освобождался более одного раза и никогда не был доступен после этого времени.
Определенно посмотрите на умные указатели, которые были изобретены, чтобы облегчить жизнь программиста только в этих обстоятельствах. (Более традиционный метод должен быть осторожным, а не прикручивать его, а затем, возможно, назначить NULL указателю, когда вы знаете, что он был удален, как говорит Алок.)
Ответ 3
В С++ Как решить или знать, был ли указатель удален до
Стандарт языка не предлагает законного способа определить, действителен ли произвольный указатель.
Там один путь, но он очень компилятор /OS -specific. Вы можете подключиться к существующему диспетчеру памяти или заменить его на свой собственный и предоставить специальную функцию для проверки указателя. Однако это может быть непросто. И вы действительно не хотите полагаться на эту функциональность, если производительность критическая.
Ответ 4
Указатель ничего не скажет. Ваш дизайн должен: if
вы используете динамическое распределение, это обычно потому, что ваш
приложение требует, чтобы объект имел определенный срок службы, поэтому
вы знаете, когда правильно удалить объект. Если объект
с возможностью копирования или имеет продолжительность жизни, которая соответствует сфере действия, вы
не (обычно) распределять его динамически.
Есть, конечно, исключения из кода очень низкого уровня: if
вы реализуете что-то вроде std::vector
, у вас будет
использовать какое-то динамическое распределение, поскольку размер не
известный во время компиляции. Но такие распределения не должны убегать;
это ответственность класса низкого уровня для
Память.
Наконец, переполнение буфера, доступ к уже удаленной памяти и
например, поведение undefined. Они вообще не делают,
приводят к исключению, и нет общего способа
обрабатывая их. (Обычно вы можете организовать сигнал, когда
такие вещи происходят, но есть так мало вещей, которые вы можете сделать из
обработчик сигнала, это не очень помогает.) В общем,
то, что вы хотите, так это для программы, чтобы сбой, так как вы не знаете
в каком состоянии оно находится. В редких случаях, когда это не
случае, вы должны отказаться от реализации, определенной
если они существуют. Если вы скомпилируете /EHa
вариант с VС++, например, то, что обычно было бы крахом
будет скрыта в исключении С++. Но что VС++
расширения, и вы все еще не знаете общего состояния
когда это произойдет. Если это произошло из-за того, что вы испортили
арене свободного пространства, там, вероятно, не так много можно сделать, даже если
вы поймаете исключение (и там есть хороший шанс, который вы получите
другое исключение из деструктора, пытающегося освободить память, когда
вы разматываете стек).
Ответ 5
использовать shared_ptr<>
и shared_array<>
, помните, что shared_ptr<>
может использоваться для управления памятью, выделенной для массива, только если соответствующий Deleter предоставлен, в противном случае используйте shared_array<>
для управления вашими массивами
A* a_tab=new A[100];
boost::shared_ptr<A> a_tab_ok(a_tab,ArrayDeleter<A>());
//только ОК, если
template <typename T>
class ArrayDeleter
{
public:
void operator () (T* d) const
{
delete [] d; //will delete array!
}
};
предоставляется
Ответ 6
Умный указатель - лучший выбор, чтобы избежать таких проблем (но у вас должно быть полное понимание, прежде чем использовать их), но я хотел бы упомянуть ограничения производительности, связанные с интеллектуальными указателями. Причина в том, что они обычно используют атомарные операции, например InterlockedIncrement в Win32 API для подсчета ссылок. Эти функции значительно медленнее, чем простая целочисленная арифметика. Я не уверен, что такой небольшой штраф за исполнение, приемлемый в вашем случае, или нет.
То, что я обычно делаю (так что мне не нужно тратить несколько дней на то, чтобы отлаживать неприятные ошибки), я трачу много времени на дизайн и время жизни объекта, прежде чем переходить к реальному кодированию, поскольку я удаляю память, которую я конкретно установить указатель на NULL, это хорошая практика, насколько я думаю. Опять же, возможно, реальное решение - потратить больше времени на определение зависимостей и времени жизни объекта, прежде чем двигаться дальше!
Ответ 7
Я знаю, что эта ветка устарела. Но если кто-то читает это, он должен знать об уникальном_ptr. У shared_ptr действительно накладные расходы. Счетчик хранится в куче. При каждом обращении к счетчику возникает риск несоответствия кеша процессора. unique_ptr Более ограниченный, но не имеет накладных расходов по сравнению с обычными указателями. Мое предложение - предпочесть unique_ptr над shared_ptr, когда вам не нужен подсчет ссылок.
Другое важное замечание состоит в том, что unique_ptr хорошо работает с массивами.
Если я правильно помню, это также верно для shared_ptr, поскольку С++ 17.