Проверка NULL перед удалением объекта с перегруженным delete

Это вышло как один из комментариев обзора кода.

хорошо ли проверять значение NULL перед вызовом delete для любого объекта?

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

11 ответов


нет, не проверяйте значение null. Стандарт говорит, что delete (T*)0; действителен. Это просто усложнит ваш код без каких-либо преимуществ. Если operator delete перегружен, лучше проверить значение null в реализации оператора. Просто сохраняет строки кода и ошибки.

EDIT: этот ответ был принят и поддержан, но, на мой взгляд, он был не очень информативным. Во всех ответах есть одна недостающая часть, и, ради совести, позвольте мне добавить эту последнюю часть здесь.

стандарт фактически говорит в [basic.НТЦ.dynamic], по крайней мере, с C++03:

любые функции распределения и/или освобождения, определенные в программе на C++, включая версии по умолчанию в библиотеке, должны соответствовать семантике, указанной в 3.7.4.1 и 3.7.4.2.

где ссылочные разделы, а также некоторые другие места в стандарте, перечисленные в других ответах, говорят, что семантика передачи нулевого указателя является нет.


Я бы сказал, что это больше повод, чтобы гарантировать, что если вы перегрузите operator delete, тогда вы всегда должны иметь его проверить на NULL, иначе вы нарушаете семантику.


хорошо ли проверять значение NULL перед вызовом delete для любого объекта?

нет!

int *p = NULL;
delete p ; //no effect

стандарт говорит [раздел 5.3.5/2]

если операнд имеет тип класса, операнд преобразуется в тип указателя путем вызова вышеупомянутой функции преобразования, а преобразованный операнд используется вместо исходного операнда для остальной части этого раздела. В любом случае,если значение операнд delete-это нулевой указатель, операция не имеет эффекта.

кроме того в разделе 18.4.1.1/13

void operator delete(void* ptr) throw();

void operator delete(void* ptr, const std::nothrow_t&) throw();

по умолчанию:

- для нулевого значения ptr ничего не делать.

- любое другое значение ptr должно быть значением, возвращенным ранее вызовом оператора по умолчанию new, который не был признан недействительным промежуточным вызов оператора delete (void*) (17.4.3.7). Для такого ненулевого значения ptr восстанавливает хранилище, выделенное предыдущим вызовом оператору по умолчанию new.

редактировать :

Джеймс Kanze здесь говорит, что

это все еще ответственность оператора delete (или delete []) для проверки; стандарт не гарантирует, что ему не будет предоставлен нулевой указатель; стандарт требует, чтобы он был no-op, если задан null указатель. Или что реализация позволяет называть это. Согласно последнему проекту, " значение первого аргумента, предоставленного функции освобождения, может быть значением нулевого указателя; если это так, и если функция освобождения предоставляется в стандартной библиотеке, вызов не имеет никакого эффекта."Я не совсем уверен, что последствия этого" один поставляется в стандартной библиотеке " означают, чтобы быть---принято буквально, так как его функция не является одним из предоставляемых стандартной библиотеки, предложение кажется, это не подходит. Но почему-то это не имеет смысла.


Я бы сказал, что это ответственность перегруженного delete вести себя так, как вы ожидаете delete вести себя. То есть, это должны обрабатывать нулевые указатели как no-op.

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


нет необходимости проверять null. оператор delete делает chck для null, поэтому дополнительная проверка не требуется.


delete (T*)0; действителен и ничего не делает, аналогично free(NULL); также является допустимым, и ничего не делает. Если вы перегрузите delete оператор, ваша реализация должна нести ту же семантику. Стандарт говорит, как стандарт delete будет работать, но я не думаю, что он говорит, как перегруженный delete должны вести себя. Для обеспечения согласованности со стандартом/поведением по умолчанию он должен разрешить (T*)0 в качестве входных данных.


из стандартных документов, 18.5.1.1.13 под delete,

по умолчанию: если ptr равен null, ничего не делает. в противном случае восстанавливает хранилище, выделенное предыдущим вызовом оператор new.

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


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

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

void MyObj::reset()
{
    delete impl_;
    impl_ = 0;    // Needed here - impl_ may be reused / referenced.
}

MyObj::~MyObj()
{
    delete impl_; // No need to assign here as impl_ is going out of scope.
}

Это не необходимо проверить. Если кто-то перегружает pethod, это его ответственность перед тем, что с NULL.


Я бы сказал, что вопросы содержат неполную информацию. В моем магазине все еще есть проверка на NULL перед удалением в нашем стандарте кодирования, поскольку у нас все еще есть одна конфигурация компилятора/платформы, которую мы должны поддерживать, которая переходит в неопределенное поведение с оператором defualt delete, если он передан NULL. Если оригинальный плакат есть аналогичная ситуация, то есть такая точка, чтобы проверить на null, в противном случае, изменить стандарт кодирования!


немного педантичности C++ : NULL не является встроенной концепцией. Да, мы все знаем, что это значит, но в C++ до C++0X концепция нулевого указателя-это просто значение 0. NULL обычно является макросом платформы, который расширяется до 0.

с C++0X мы получаем nullptr, который яснее простого нуля и не конвертируется в любой интегральный тип, кроме bool, и является лучшей концепцией, чем NULL (или, возможно, лучшей реализацией концепции за NULL).