Ответ
Это абсолютно безопасная и корректная операция. Согласно стандарту C++ (начиная с C++11), вызов delete или delete[] на нулевом указателе (значении nullptr или унаследованном NULL) не имеет никакого эффекта (является no-op).
Почему это полезно: Это избавляет от необходимости явных проверок перед удалением.
MyClass* ptr = nullptr;
// ... какой-то код, который может или не может выделить память ...
delete ptr; // Всегда безопасно. Если ptr == nullptr, ничего не происходит.
Важные уточнения:
- До C++11 поведение было неопределённым (Undefined Behavior), хотя большинство реализаций также обрабатывали это безопасно. Для полной переносимости в старом коде иногда делали проверку
if (ptr) delete ptr;. - Двойное удаление (double delete) — это всегда UB, даже если первый вызов был на
nullptr.int* p = new int(42); delete p; // Освобождает память p = nullptr; // Обнуляем указатель delete p; // Безопасно, ничего не делает (p == nullptr) // НО: delete p; // Второй вызов delete на том же нулевом указателе — всё ещё безопасный no-op. - Это правило работает только для стандартного оператора
delete. Для пользовательских функций освобождения памяти (placement delete, custom deallocators) поведение определяется их реализацией.
Ответ 18+ 🔞
Давай разберём эту тему, чтобы не было потом мучительно больно за вылетевшую программу. Всё проще, чем кажется, если не лезть в дебри.
Представь себе: у тебя есть указатель. Он либо показывает на кучу памяти, либо в никуда (nullptr). И ты такой: "Так, надо почистить за собой". Раньше, в каменном веке до C++11, народ писал проверки, типа:
if (myPtr != nullptr) {
delete myPtr;
}
А сейчас? Ёпта, да похуй! Серьёзно. Стандарт говорит — вызывай delete на nullptr, и ничего не произойдёт. Вообще. Никакого эффекта. Это как крикнуть в пустую комнату — звук есть, а последствий ноль.
MyClass* ptr = nullptr;
// ... тут могла быть твоя жизнь, а могла и не быть ...
delete ptr; // Абсолютно безопасно. Если ptr нулевой, компилятор просто пожмёт плечами и пойдёт дальше.
Но есть нюансы, блядь, куда же без них:
-
Исторический экскурс. До 2011 года формально это было неопределённым поведением. Хотя все компиляторы делали вид, что так и надо, и тоже ничего не делали. Так что если видишь в легаси-коде проверку перед
delete— теперь понимаешь, откуда у неё ноги растут. Это пережиток прошлого, как кнопка «Сохранить» в виде дискеты. -
Главная ловушка — двойное удаление. Вот тут уже будет тебе хиросима и нигасраки. Это UB (Undefined Behavior) в чистом виде. Даже если ты после первого удаления занулил указатель.
int* p = new int(42); delete p; // Освободил память. В этот момент p уже "висячий" указатель (dangling). p = nullptr; // Занулил. Молодец. delete p; // Удалил nullptr. Безопасно, ничего не делаем. // Всё ок? Нет, блядь! // А если бы не занулил? delete p; // Второй раз по тому же адресу? Пиздец. Скрепы пошли.Суть в том, что опасен не второй вызов
deleteнаnullptr, а второй вызовdeleteна одну и ту же, уже освобождённую память. Зануление — это просто хороший тон, чтобы не наступить на эти грабли случайно. -
Это работает только для стандартного
delete. Если ты какой-нибудь хитрый жопа и написал свой аллокатор или используешь placement new, то там свои правила. Твой кастомный механизм очистки может и не ожидать нулевого указателя. Так что тут уже без страховки.
Итог: С современным C++ можешь не париться. delete nullptr — это легально, безопасно и удобно. Главное — следи, чтобы не удалить одну память дважды, вот где собака зарыта.