Безопасно ли удалять нулевой указатель?

263

Можно ли удалить указатель NULL?

И это хороший стиль кодирования?

  • 14
    Хорошей практикой является написание программ на C ++ без единого вызова delete . Вместо этого используйте RAII . То есть используйте std::vector<T> v(100); вместо T* p = new T[100]; используйте умные указатели, такие как unique_ptr<T> и shared_ptr<T> которые заботятся об удалении, вместо необработанных указателей и т. д.
  • 8
    благодаря make_shared (c ++ 11) и make_unique (c ++ 14) ваша программа должна содержать ноль new и delete
Показать ещё 4 комментария
Теги:
pointers
memory-management
delete-operator
null-pointer

7 ответов

210
Лучший ответ

delete выполняет проверку в любом случае, поэтому проверка на вашей стороне добавляет лишние накладные расходы и выглядит уродливее. Хорошая практика очень устанавливает указатель на NULL после delete (помогает избежать двойного удаления и других подобных проблем с повреждением памяти).

Мне также понравилось бы, если бы delete по умолчанию устанавливал параметр NULL, как в

#define my_delete(x) {delete x; x = NULL;}

(Я знаю о значениях R и L, но было бы неплохо?)

  • 68
    Обратите внимание, что все еще могут быть несколько других указателей, указывающих на один и тот же объект, даже если вы устанавливаете один в NULL при удалении.
  • 2
    @ Конечно, но все же лучше, чем ничего. В большинстве случаев есть только один указатель.
Показать ещё 31 комментарий
72

Из проекта стандарта С++ 0x.

$5.3.5/2 - "[...] В любом случае альтернатива, значение операнда удаления может быть нулевым указателем значение. [..."

Конечно, никто не будет "удалять" указателя со значением NULL, но это безопасно. В идеале не должно быть кода, который бы удалял указатель NULL. Но иногда полезно, когда удаление указателей (например, в контейнере) происходит в цикле. Так как удаление значения указателя NULL безопасно, можно действительно написать логику удаления без явных проверок для удаления операнда NULL.

В качестве отступления, стандарт C $7.20.3.2 также говорит, что "свободный" по указателю NULL не выполняет никаких действий.

Свободная функция вызывает пробел на который указывает ptr, который должен быть освобожден, то есть доступный для дальнейшего распределение. Если ptr - нулевой указатель, никаких действий не происходит.

  • 1
    Мне бы очень хотелось, чтобы этот ответ содержался в цитатах, если он намеренно не привел к неэффективности неоптимизированного кода. Как говорится в принятом ответе, удаление нулевого указателя не допускается. Поэтому проверка, является ли указатель нулевым, перед его удалением является абсолютно посторонним.
44

Да, это безопасно.

Нет вреда при удалении нулевого указателя; он часто уменьшает количество тестов в хвосте функции, если нераспределенные указатели инициализируются до нуля, а затем просто удаляются.


Поскольку предыдущее предложение вызвало путаницу, пример - который не является безопасным исключением - того, что описывается:

void somefunc(void)
{
    SomeType *pst = 0;
    AnotherType *pat = 0;

    …
    pst = new SomeType;
    …
    if (…)
    {
        pat = new AnotherType[10];
        …
    }
    if (…)
    {
        …code using pat sometimes…
    }

    delete[] pat;
    delete pst;
}

Есть все виды нит, которые можно выбрать с помощью кода примера, но концепция (я надеюсь) понятна. Переменные указателя инициализируются на ноль, так что операциям delete в конце функции не нужно проверять, не являются ли они ненужными в исходном коде; код библиотеки выполняет проверку в любом случае.

  • 0
    Я должен был прочитать это несколько раз, чтобы понять это. Вы должны иметь ввиду инициализацию их нулями в начале метода или во время, а не в хвосте, конечно? В противном случае вы просто удалите как обнуление, так и удаление.
  • 1
    @EJP: не совсем неправдоподобный план функции может быть следующим: void func(void) { X *x1 = 0; Y *y1 = 0; … x1 = new[10] X; … y1 = new[10] Y; … delete[] y1; delete[] x1; } Я не показывал никакой блочной структуры или переходов, но операции delete[] в конце безопасны из-за инициализации в начале. Если что-то подпрыгнуло до конца после того, как был выделен x1 и до того, как был выделен y1 и не было инициализации y1 , тогда было бы неопределенное поведение - и хотя код мог проверять на нулевое значение ( x1 и y1 ) перед удалением, нет необходимости делать это.
19

Удаление нулевого указателя не имеет эффекта. Это не очень хороший стиль кодирования, потому что он не нужен, но это тоже неплохо.

Если вы ищете хорошие методы кодирования, подумайте об использовании интеллектуальных указателей, а затем вам не нужно delete вообще.

  • 17
    время, когда люди хотят удалить указатель NULL, это когда они не уверены, содержит ли он NULL ... если бы они знали, что это NULL, то они бы не рассматривали удаление и, следовательно, спрашивали ;-).
  • 0
    @ Тони: Я только хотел сказать, что это не будет иметь никакого эффекта, и наличие такого кода, который удаляет указатель, который иногда содержит NULL, не обязательно плох.
Показать ещё 2 комментария
3

Чтобы дополнить ruslik ответ, в С++ 14 вы можете использовать эту конструкцию:

delete std::exchange(heapObject, nullptr);
2

Это безопасно, если вы не перегрузили оператора удаления. если вы перегрузили оператор delete и не обрабатывали нулевое состояние, это небезопасно.

  • 0
    Не могли бы вы добавить какое-либо объяснение вашего ответа?
  • 0
    Вы имеете в виду, что это безопасно, если вы не перегружены удалить?
0

Я испытал, что небезопасно (VS2010) удалять [] NULL (т.е. синтаксис массива). Я не уверен, соответствует ли это стандарту С++.

Безопасно удалять NULL (скалярный синтаксис).

  • 6
    Это незаконно, и я не верю этому.
  • 5
    Вы должны быть в состоянии удалить любой нулевой указатель. Так что, если он ломается для вас, то, вероятно, у вас есть ошибка в коде, который показывает это.
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню