Там какая-либо причина состоит в том, чтобы проверить на Нулевого указателя перед удалением?

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

Thing th(6, thing_ptr);

, у вас нет выбора в отношении времени жизни этого объекта: он заканчивается в следующем заключении }, если объявлен в функциональном блоке или с концом времени жизни содержащегося объекта, если он объявлен как член класса, или в конце программы, если он объявлен как член пространства имен. std::unique_ptr<T> с использованием средства удаления по умолчанию может содержать только указатель на объект, созданный с использованием new, поскольку средство удаления по умолчанию пытается использовать delete.

Если ваши указатели всегда будут на объекты с обычными объявлениями, подобными этим, и не созданы с использованием new, то вы застряли, убедившись, что указатели используются только в течение времени жизни объектов, на которые они указывали в. И в этом случае не так уж много преимуществ от использования std::unique_ptr; вы можете пойти дальше и использовать необработанный указатель, который по-прежнему является обычным способом представления указателя, когда что-то еще (здесь сам язык C ++) отвечает за проблемы жизни.

Если вам нужна возможность указателей, которые переживают функциональный блок, в котором создается объект, вы не можете использовать обычный синтаксис объявления объекта. Одна из возможных альтернатив состоит в том, чтобы при создании объекта использовать функцию create вместо прямого объявления объекта:

class Thing {
public:
    static std::unique_ptr<Thing> create(const Thing& src)
    { return { new Thing(src) }; }
    static std::unique_ptr<Thing> create(int data)
    { return { new Thing(data) }; }

    void PrintData() const;

private:
    // All constructors private, to make sure a Thing can ONLY be
    // created by a create() function - including the copy constructor.
    Thing(const Thing&) = default;
    explicit Thing(int data) : data_(data) {}

    int data_;
};

int main() {
    auto thing_ptr = Thing::create(6);
    thing_ptr->PrintData();
}
49
задан Sam 22 August 2014 в 23:10
поделиться

9 ответов

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

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

67
ответ дан Randolpho 7 November 2019 в 11:29
поделиться

Стандарт C++ гарантирует, что законно использовать нулевого указателя в удалять-выражение (В§8.5.2.5/2). Однако это неуказанное , вызовет ли это функцию освобождения (operator delete или operator delete[]; В§8.5.2.5/7, отметьте).

, Если функция освобождения по умолчанию (т.е. обеспеченный стандартной библиотекой) вызвана с нулевым указателем, то вызов не имеет никакого эффекта (В§6.6.4.4.2/3).

, Но это является неуказанным, что происходит, если функция освобождения не обеспечивается стандартной библиотекой — В т.е. что происходит, когда мы перегружаемся operator delete (или operator delete[]).

А компетентный программист обработал бы нулевых указателей соответственно внутренний функция освобождения, а не перед вызовом, как показано в коде OP’s. Аналогично, устанавливая указатель на [1 110] / NULL после того, как удаление только служит очень ограниченной цели. Некоторым людям нравится делать это в духе [1 127] безопасное программирование : это сделает поведение программы немного более предсказуемым в случае ошибки: доступ к указателю после удаления приведет к доступу к нулевому указателю, а не доступу к случайной ячейке памяти. Хотя обе операции являются неопределенным поведением, поведение доступа к нулевому указателю намного более предсказуемо на практике (это чаще всего приводит к прямому катастрофическому отказу, а не повреждению памяти). Так как повреждения памяти особенно трудно отладить, сбрасывание удаленных указателей помогает отладке.

—, Конечно, это лечит симптом, а не причину (т.е. ошибка). необходимо рассматривать указатели сброса как запах кода. Чистый, современный код C++ ясно даст понять владение памяти и статически проверенный (при помощи интеллектуальных указателей или эквивалентных механизмов), и таким образом доказуемо избежит этой ситуации.

Премия: объяснение перегруженных operator delete:

operator delete (несмотря на его имя) функция, которая может быть перегружена как любая другая функция. Эта функция вызвана внутренне для каждого вызова [1 114] с соответствием аргументам. То же верно для [1 115].

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

На данный момент, позвольте нам просто сказать, что мы хотим перегрузить глобальную версию [1 118] для отладки. Прежде чем мы сделаем это, одно короткое уведомление о том, что происходит в следующем коде:

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 = custom_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;
    custom_free(p);
}

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

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

Это привело к следующему выводу:

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

Теперь, этот код делает что-то существенно различное, чем стандартная реализация [1 124]: Это не протестировало на нулевых указателей! компилятор не проверяет это так вышеупомянутые компиляции кода, но он может дать противные ошибки во времени выполнения, когда Вы пытаетесь удалить нулевых указателей.

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

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

В заключение хотя неаккуратная реализация [1 126] может потребовать, явный пустой указатель регистрируется в клиентском коде, это - нестандартное поведение и должно только быть допущено в поддержке прежней версии ( если во всем ).

37
ответ дан Konrad Rudolph 7 November 2019 в 11:29
поделиться

Удалите проверки на ПУСТОЙ УКАЗАТЕЛЬ внутренне. Ваш тест избыточен

9
ответ дан EvilTeach 7 November 2019 в 11:29
поделиться

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

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

7
ответ дан Welbog 7 November 2019 в 11:29
поделиться

Если pSomeObject будет ПУСТЫМ, удалите, ничего то не сделает. Так не, Вы не должны проверять на ПУСТОЙ УКАЗАТЕЛЬ.

Мы считаем это хорошей практикой для присвоения ПУСТОГО УКАЗАТЕЛЯ указателю после удаления его, если вообще возможно, что некоторый knucklehead может попытаться использовать указатель. Используя Нулевого указателя немного лучше, чем использование указателя на то, кто знает то, что (Нулевой указатель вызовет катастрофический отказ, указатель на удаленную память не может)

3
ответ дан Joe 7 November 2019 в 11:29
поделиться

Нет никакой причины проверить на ПУСТОЙ УКАЗАТЕЛЬ до, удаляют. Присвоение ПУСТОГО УКАЗАТЕЛЯ после удаляет, могло бы быть необходимым, если где-нибудь в проверках кода сделаны, выделяется ли некоторый объект уже путем выполнения ПУСТОЙ проверки. Примером были бы своего рода кэшированные данные, которые выделяются по требованию. Каждый раз, когда Вы убираете объект кэша, Вы присваиваете ПУСТОЙ УКАЗАТЕЛЬ указателю так код, который выделяет объект, знает, что он должен выполнить выделение.

1
ответ дан VoidPointer 7 November 2019 в 11:29
поделиться

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

if(pSomeObject1!=NULL) pSomeObject1=NULL;

, Но тогда удаляют, делает то точное сравнение так или иначе (выполнение ничего, если это является ПУСТЫМ). Почему делают это дважды? Можно всегда присваивать pSomeObject ПУСТОМУ УКАЗАТЕЛЮ после того, как вызов удаляет, независимо от его текущего значения - но это было бы немного избыточно, если бы он уже имел то значение.

, Таким образом, моя ставка является автором тех строк, которые попробовали, чтобы удостовериться, что pSomeObject1 всегда будет ПУСТЫМ, будучи удаленным, не подвергаясь стоимости потенциально ненужного теста и присваивания.

0
ответ дан Joe Pineda 7 November 2019 в 11:29
поделиться

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

ОПИСАНИЕ

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

Так рассматривают освобождение NULL указатели как ошибка, и Вы будете в безопасности.

-2
ответ дан Hosam Aly 7 November 2019 в 11:29
поделиться

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

-5
ответ дан 7 November 2019 в 11:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: