Удалить на уже удаленном объекте: поведение? - PullRequest
4 голосов
/ 14 декабря 2011

Мне интересно, что произойдет, если я попытаюсь сделать delete для указателя, который уже удален или, возможно, не был выделен? Я прочитал две вещи: во-первых, оператор delete выполнит некоторые проверки, и нам не нужно проверять, равен ли указатель нулю; а потом я прочитал, что это может привести к неизвестному поведению ..

Я спрашиваю это, потому что я использую некоторые личные объекты, которые содержат атрибуты объектов Qt; Я думаю, что Qt удаляет все виджеты, связанные, когда мы закрываем окно, но я не совсем уверен и до сих пор: если мягкий сбой до закрытия окна, мы должны удалить все объекты вручную.

Итак, что будет лучшим решением? Что-то подобное?

if( my_object )
    delete my_object;

Можно ли избежать опасного поведения?

Ответы [ 6 ]

14 голосов
/ 14 декабря 2011

delete для уже delete d ненулевого указателя - неопределенное поведение - ваша программа, вероятно, вылетит.Вы можете безопасно использовать delete для нулевого указателя - это приведет к неработоспособности.

Так что настоящая проблема не в delete для нулевого указателя.Настоящая проблема здесь:

 ptr = new Something();
 otherPtr = ptr;
 delete ptr;
 delete otherPtr;

Это может произойти, если у вас есть несколько указателей на один и тот же объект, и это довольно опасно.Возможные решения:

  • использовать умные указатели (в вашем коде нет delete) или
  • иметь только один назначенный указатель для управления временем жизни каждого объекта и delete точнонужное время.
4 голосов
/ 14 декабря 2011
if( my_object )
    delete my_object;

является избыточным.delete на указателе NULL ничего не делает.Это гарантируется стандартом.

delete для указателя, который уже был удален, вызывает неопределенное поведение.Вот почему вы всегда должны помнить, чтобы назначить указатели на NULL после их удаления:

delete p;
p = NULL;

РЕДАКТИРОВАТЬ: Согласно комментариям, я чувствую, что должен указать это.Если у вас есть несколько указателей на один и тот же объект, присваивание NULL не сделает удаление безопасным.В любом случае, лучше использовать умные указатели.

1 голос
/ 14 декабря 2011

правильный путь:

if( my_object )
{
    delete my_object;
    my_object = NULL;
}

потому что, вызов дважды, как это было раньше, вызовет delete для указателя deleted.

1 голос
/ 14 декабря 2011

Обратите внимание, что удаление указателя не устанавливает его в NULL.

int* i = new int;
*i = 42;
delete i;
delete i; // oops! i is still pointing to the same memory, but it has been deleted already

Удаление нулевого указателя ничего не делает, удаление уже удаленного объекта приведет к неопределенному поведению.

0 голосов
/ 14 декабря 2011

Просто чтобы объединить ответы выше:

  • if (my_object) проверяет значение указателя, а не существование объекта. Если он уже удален, указатель все еще может указывать на это местоположение.
  • удаление уже удаленного объекта является неопределенным поведением и, вероятно, приведет к падению вашей программы.
  • удаление NULL определено и ничего не делает. Вместе с пунктом 1 это объясняет ответ Лучиана.

Подводя итог: вы должны четко понимать, кому принадлежит объект и где находятся различные указатели на объект. Когда вы удаляете объект, убедитесь, что все указатели, указывающие на это местоположение, равны 0 / NULL. Используйте управляющие объекты, такие как boost :: shared_pointer или QPointer, чтобы помочь вам в этой задаче.

0 голосов
/ 14 декабря 2011

Результатом будет Неопределенное поведение , если вы наберете delete для уже delete d указателя.
Вызов delete для указателя NULL не имеет никакого эффекта.

Стандарт с ++ 03 § 3.7.4.2-3

Если функция освобождения завершается выдачей исключения, поведение не определено. Значение первого аргумента, переданного функции освобождения, может быть значением нулевого указателя; если так, и если функция освобождения предоставлена ​​в стандартной библиотеке, вызов не имеет никакого эффекта. В противном случае предоставленное значение для оператора delete(void*) в стандартной библиотеке должно быть одно из значений, возвращаемых предыдущим вызовом оператора new(std::size_t) или operator new(std::size_t, const std::nothrow_-t&) в стандартной библиотеке, а значение, предоставляемое оператору delete[](void*) в стандартной библиотеке, должно быть одно из значений, возвращаемых предыдущим вызовом либо operator new[](std::size_t), либо operator new[](std::size_t, const std::nothrow_t&) в стандартной библиотеке.

Использование RAII и интеллектуальных указателей - ваше лучшее оружие, чтобы избежать подобных проблем.

...