Подтвердить что ты не робот

Есть ли причина проверить указатель NULL перед удалением?

Я часто вижу проверку устаревшего кода для NULL перед удалением указателя, аналогичного,

if (NULL != pSomeObject) 
{
    delete pSomeObject;
    pSomeObject = NULL;
}

Есть ли какая-нибудь причина для проверки указателя NULL перед удалением? Какова причина установки указателя на NULL после?

4b9b3361

Ответ 1

Совершенно "безопасно" для удаления нулевого указателя; он фактически равен нулю.

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

Ответ 2

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

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

Объяснение перегруженного operator delete:

operator delete - это (несмотря на свое имя) функция, которая может быть перегружена, как любая другая функция. Эта функция вызывается внутренне для каждого вызова operator delete с соответствующими аргументами. То же самое верно для operator new.

Перегрузка operator new (а затем также operator delete) имеет смысл в некоторых ситуациях, когда вы хотите точно контролировать распределение памяти. Это не очень сложно, но необходимо принять несколько мер предосторожности, чтобы обеспечить правильное поведение. Скотт Майерс описывает это очень подробно. Эффективный С++.

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

klass* pobj = new klass;
// … use pobj.
delete pobj;

Что на самом деле происходит здесь? Хорошо приведенное выше можно приблизительно перевести на следующий код:

// 1st step: allocate memory
klass* pobj = static_cast<klass*>(operator new(sizeof(klass)));
// 2nd step: construct object in that memory, using placement new:
new (pobj) klass();

// … use pobj.

// 3rd step: call destructor on pobj:
pobj->~klass();
// 4th step: free memory
operator delete(pobj);

Обратите внимание на шаг 2, где мы вызываем new со слегка нечетным синтаксисом. Это вызов так называемого размещения new, который берет адрес и строит объект по этому адресу. Этот оператор также может быть перегружен. В этом случае он просто служит для вызова конструктора класса klass.

Теперь, без дальнейшего использования здесь код для перегруженной версии операторов:

void* operator new(size_t size) {
    // See Effective C++, Item 8 for an explanation.
    if (size == 0)
        size = 1;

    cerr << "Allocating " << size << " bytes of memory:";

    while (true) {
        void* ret = malloc(size);

        if (ret != 0) {
            cerr << " @ " << ret << endl;
            return ret;
        }

        // Retrieve and call new handler, if available.
        new_handler handler = set_new_handler(0);
        set_new_handler(handler);

        if (handler == 0)
            throw bad_alloc();
        else
            (*handler)();
    }
}

void operator delete(void* p) {
    cerr << "Freeing pointer @ " << p << "." << endl;
    free(p);
}

Этот код просто использует malloc/free внутренне, как и большинство реализаций по умолчанию. Он также создает отладочный вывод. Рассмотрим следующий код:

int main() {
    int* pi = new int(42);
    cout << *pi << endl;
    delete pi;
}

Он дал следующий результат:

Allocating 4 bytes of memory: @ 0x100160
42
Freeing pointer @ 0x100160.

Теперь этот код делает что-то принципиально иное, чем стандартная реализация operator delete: Он не тестировал нулевые указатели! Компилятор не проверяет это, поэтому приведенный выше код компилируется, но он может дать неприятные ошибки во время выполнения, когда вы пытаетесь удалить нулевые указатели.

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

void operator delete(void* p) {
    if (p == 0) return;
    cerr << "Freeing pointer @ " << p << "." << endl;
    free(p);
}

В заключение, хотя sloppy-реализация operator delete может потребовать явных проверок null в клиентском коде, это нестандартное поведение и должно допускаться только в старой поддержке (если вообще).

Ответ 3

Удаление null - это не-op. Нет причин проверять значение null перед вызовом delete.

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

Ответ 4

Удалить проверки для NULL внутренне. Ваш тест является избыточным.

Ответ 5

В соответствии с С++ 03 5.3.5/2 безопасно удалять нулевой указатель. Это указано в стандарте:

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

Ответ 6

Если pSomeObject имеет значение NULL, delete ничего не сделает. Так что нет, вам не нужно проверять наличие NULL.

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

Ответ 7

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

Ответ 8

Это зависит от того, что вы делаете. Например, некоторые старые версии free не будут счастливы, если они передаются указателем NULL. У некоторых библиотек все еще есть эта проблема. Например, XFree в библиотеке Xlib говорит:

ОПИСАНИЕ

Функция XFree является универсальная процедура Xlib, которая освобождает указанные данные. Вы должны использовать его для освобождения любых объектов, которые были выделенных Xlib, если только альтернативный функция явно указана для объект. Указатель NULL не может быть передается этой функции.

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

Ответ 9

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

if(pSomeObject1!=NULL) pSomeObject1=NULL;

Но тогда delete делает точное сравнение в любом случае (ничего не делая, если оно NULL). Зачем это делать дважды? Вы всегда можете назначить pSomeObject в NULL после вызова delete, независимо от его текущего значения, но это было бы немного избыточным, если бы оно уже имело это значение.

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

Ответ 10

Как и в моих наблюдениях, удаление нулевого указателя с использованием delete безопасно в машинах на базе unix, таких как PARISC и itanium. Но это довольно опасно для Linux-систем, так как тогда процесс будет аварийно завершен.