Ir para o conteúdo

Última atualização:2026-10-12

Inicialização e atribuição em 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 levanta algumas perguntas:

  • Por que o contador do ponteiro inteligente construído a partir do ponteiro bruto nullptr não é 0?
  • delete nullptr é seguro?

1.Semântica de um shared_ptr vazio

Vejamos diretamente o código-fonte do shared_ptr: o chamado vazio refere-se a:

  • construir um shared_ptr por padrão
  • construir um shared_ptr a partir de um nullptr_t

Somente nesses dois casos ele é vazio; já um shared_ptr construído a partir de um ponteiro bruto é dono da propriedade desse ponteiro, esteja ele vazio ou não.

Correspondem, respectivamente, aos três construtores a seguir.

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)
    {}

No caso acima, em que o ponteiro bruto é nullptr, o que acontece ao executar delete nullptr? E o que aconteceria se fosse excluído duas vezes?

2.delete nullptr

Suponha o código abaixo: após a execução, ele falhará (crash) ou rodará normalmente?

#include <iostream>

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

}

A resposta é: a saída é normal.

O motivo específico está no 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

Se a expressão avaliar para um valor de ponteiro nulo, nenhum destrutor é chamado, e a função de desalocação pode ou não ser chamada (é um comportamento não especificado), mas as funções de desalocação padrão garantidamente não fazem nada quando recebem um ponteiro nulo.

Dica de leitura

Este artigo discute a diferença entre inicialização (initialization) e atribuição (assignment) em C++; veja também Lista de inicialização de classes.

Tópicos relacionados

Comentários