Перейти к содержанию

Последнее обновление:2026-10-12

C++ инициализация и присваивание

C++ iv

int* p = nullptr;
std::shared_ptr<int> s(p); // 计数器1
std::shared_ptr<int> s2(s); // 计数器2
assert(s.use_count() == 2); 

Этот код вызывает несколько вопросов:

  • Почему счётчик умного указателя, сконструированного из nullptr-сырого-указателя, не равен 0?
  • Безопасно ли делать delete nullptr?

1.Семантика пустого shared_ptr

Посмотрим прямо на исходный код shared_ptr; пустым считается:

  • shared_ptr, сконструированный по умолчанию
  • shared_ptr, сконструированный из nullptr_t

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

Этим соответствуют три приведённых ниже конструктора.

private:
    element_type*      __ptr_;
    __shared_weak_count* __cntrl_;

public:
    _LIBCPP_HIDE_FROM_ABI
    _LIBCPP_CONSTEXPR shared_ptr() _NOEXCEPT
        : __ptr_(nullptr),
          __cntrl_(nullptr)
    { }

    _LIBCPP_HIDE_FROM_ABI
    _LIBCPP_CONSTEXPR shared_ptr(nullptr_t) _NOEXCEPT
        : __ptr_(nullptr),
          __cntrl_(nullptr)
    { }
    shared_ptr(_Yp* __p, _Dp __d)
        : __ptr_(__p)
    {}

Если в описанном выше случае сырой указатель равен nullptr, что произойдёт при выполнении delete nullptr? А при удалении дважды?

2.delete nullptr

Пусть есть следующий код — он упадёт после запуска? Или отработает нормально?

#include <iostream>

int main() {
    int *p = nullptr;
    delete p;
    std::cout << "delete once" << std::endl;
    delete p;
    std::cout << "delete twice" << std::endl;

}

Ответ: нормальный вывод.

Конкретную причину смотрите в cppreference:

If expression evaluates to a null pointer value, no destructors are called, and the deallocation function may or may not be called (it's unspecified), but the default deallocation functions are guaranteed to do nothing when passed a null pointer.

https://en.cppreference.com/w/cpp/language/delete

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

Совет по чтению

В этой статье обсуждается различие между инициализацией (initialization) и присваиванием (assignment) в C++; см. также список инициализации класса.

Похожие темы

Комментарии