Какая версия safe_delete лучше? - PullRequest
5 голосов
/ 12 февраля 2009
#define SAFE_DELETE(a) if( (a) != NULL ) delete (a); (a) = NULL;

OR

template<typename T> void safe_delete(T*& a) {
  delete a;
  a = NULL;
}

или любым другим лучшим способом

Ответы [ 9 ]

18 голосов
/ 12 февраля 2009

Я бы сказал ни того, ни другого, поскольку оба дают ложное чувство безопасности. Например, предположим, у вас есть функция:

void Func( SomePtr * p ) {
  // stuff
  SafeDelete( p );
}

Вы устанавливаете p в NULL, но копии p вне функции не затрагиваются.

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

7 голосов
/ 12 февраля 2009

удалить;

ISO C ++ указывает, что удаление по нулевому указателю ничего не делает.

Цитата из ISO 14882:

    5.3.5 Delete [expr.delete]

    2   [...] In either alternative, if the value of the operand of delete is the 
        null pointer the operation has no effect. [...]

С уважением, Бодо

/ edit: я не заметил a = NULL; в оригинальном сообщении, поэтому новая версия: удалить; а = NULL; однако проблема с установкой a = NULL уже была указана (ложное чувство безопасности).

6 голосов
/ 12 февраля 2009

Понятно, функция по простой причине. Макрос оценивает свой аргумент несколько раз. Это может иметь злые побочные эффекты. Также функция может быть ограничена. Ничего лучше этого:)

5 голосов
/ 12 февраля 2009

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

Конечно, иногда шаблоны и функции не подходят, но здесь это не так.

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

( edit ) Как уже отмечали другие, безопасное удаление небезопасно, так как даже если кто-то не забудет его использовать, оно все равно может не дать желаемого эффекта , Так что это на самом деле совершенно бесполезно, потому что для правильного использования safe_delete нужно больше думать, чем просто установить 0 самостоятельно.

2 голосов
/ 12 февраля 2009

Я думаю

#define SAFE_DELETE(pPtr) { delete pPtr; pPtr = NULL } лучше

  1. нормально вызывать delete, если pPtr равен NULL. Так что , если проверка не требуется.
  2. в случае, если вы вызовете SAFE_DELETE (ptr + i), это приведет к ошибке компиляции.
  3. Определение шаблона создаст несколько экземпляров функции для каждого типа данных. По моему мнению, в этом случае эти множественные определения не добавляют никакой ценности.
  4. Кроме того, с определением шаблонной функции у вас возникают накладные расходы на вызов функции.
2 голосов
/ 12 февраля 2009

Вам не нужно проверять на ничтожность с delete, это эквивалентно неработоспособности. (a) = NULL заставляет меня поднять бровь. Второй вариант лучше.

Однако, если у вас есть выбор, вы должны использовать умные указатели, например std::auto_ptr или tr1::shared_ptr, которые уже делают это для вас.

1 голос
/ 20 марта 2009

Использование SAFE_DELETE действительно является подходом программистов на C для управления встроенным управлением памятью в C ++. Мой вопрос: разрешит ли C ++ этот метод использования SAFE_DELETE для указателей, которые были должным образом инкапсулированы как частные? Будет ли этот макрос работать ТОЛЬКО на указателе, объявленном Public? ООП ПЛОХО !!

0 голосов
/ 12 июня 2011

Я предпочитаю эту версию:

~scoped_ptr() {
    delete this->ptr_; //this-> for emphasis, ptr_ is owned by this
}

Установка нулевого указателя после его удаления совершенно бессмысленна, поскольку единственная причина, по которой вы будете использовать указатели, состоит в том, чтобы позволить объекту ссылаться в нескольких местах одновременно. Даже если указатель в одной части программы равен 0, вполне могут быть и другие, для которых не установлено значение 0.

Кроме того, шаблон макроса / функции safe_delete очень трудно использовать правильно, потому что есть только два места, которые он может использовать, если есть код, который может бросить между новым и удаленным для данного указателя.

1) Внутри блока catch (...), который перебрасывает исключение, а также дублируется рядом с блоком catch (...) для пути, который не выбрасывает. (Также дублируется рядом с каждым разрывом, возвратом, продолжением и т. Д., Что может позволить указателю выпасть из области видимости)

2) Внутри деструктора для объекта, которому принадлежит указатель (если нет кода между new и delete, который может генерировать).

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

Вариант 1 создает так много дублирования кода и так легко ошибиться, что я даже сомневаюсь назвать его опцией.

Опция 2 делает safe_delete избыточным, поскольку значение ptr_, которое вы устанавливаете в 0, выйдет из области видимости на следующей строке.

В итоге - не используйте safe_delete, так как он вообще не безопасен (его очень сложно правильно использовать и приводит к избыточному коду, даже когда его использование корректно). Используйте SBRM и умные указатели.

0 голосов
/ 12 февраля 2009

Как уже упоминалось выше, второй является лучшим, а не макрос с потенциальными непреднамеренными побочными эффектами, не имеет ненужной проверки на NULL (хотя я подозреваю, что вы делаете это как проверку типа), и т.д. Но ни один не обещает никакой безопасности. Если вы используете что-то вроде tr1 :: smart_ptr, пожалуйста, убедитесь, что вы прочитали документы на них и уверены, что он имеет правильную семантику для вашей задачи. Мне только недавно пришлось выследить и устранить огромную утечку памяти из-за того, что сотрудник вставил smart_ptrs в структуру данных с круговыми ссылками :) (он должен был использовать слабый_птр для обратных ссылок)

...