Saltar a contenido

Última actualización:2026-10-12

Inicialización y asignación en 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); 
Este código plantea varias dudas:

  • ¿Por qué el contador del puntero inteligente construido a partir de un puntero en bruto nullptr no vale 0?
  • ¿Es seguro hacer delete nullptr?

1. Semántica del shared_ptr vacío

Si miramos directamente el código fuente de shared_ptr, lo que se considera vacío significa:

  • Construir un shared_ptr con el constructor por defecto
  • Construir un shared_ptr a partir de nullptr_t

Solo estos dos casos se consideran vacíos; en cambio, un shared_ptr construido a partir de un puntero en bruto toma la posesión de ese puntero, sea o no nulo.

Corresponden respectivamente a los tres constructores siguientes.

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)
    {}
En el caso anterior, con el puntero en bruto siendo nullptr, ¿qué ocurre al hacer delete nullptr? ¿Y qué ocurre si se elimina dos veces?

2. delete nullptr

Supongamos el siguiente código: ¿al ejecutarlo se bloqueará? ¿O funcionará con normalidad?

#include <iostream>

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

}
La respuesta: salida normal.

La razón concreta, según 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

Si la expresión se evalúa como un valor de puntero nulo, no se llama a ningún destructor, y la función de desasignación puede llamarse o no (no está especificado), pero las funciones de desasignación por defecto garantizan no hacer nada cuando se les pasa un puntero nulo.

Consejo de lectura

Este artículo trata de la diferencia en C++ entre inicialización (initialization) y asignación (assignment); puede consultarse también la lista de inicialización de clases.

Temas relacionados

Comentarios