c++ - Como shared_ptr<void> sabe qual destruidor usar?




shared-ptr (2)

O shared_ptr sabe apenas como lidar com um objeto de gerenciamento com uma interface conhecida. Esse objeto de gerenciamento fornece duas contagens de referência (fraco para si mesmo, forte para o objeto gerenciado), além de conter o deleter (o acesso além de chamá-lo é fornecido apenas se o tipo for conhecido) e o ponteiro a ser excluído (privado) .

Que tipo e objeto o shared_ptr aponta é uma preocupação completamente separada do objeto de gerenciamento que ele usa, embora, por questões de sanidade, não deva durar mais.

Esta pergunta já tem uma resposta aqui:

Eu escrevi o código a seguir para ver como um shared_ptr<void> se comportaria quando for a última referência a shared_ptr<Thing> e ele próprio será destruído.

#include <iostream>
#include <string>
#include <memory>

using namespace std;

struct Thing{
    ~Thing(){
        cout<<"Destroyed\n";
    }
    int data;
};

int main(){
    {
        shared_ptr<void> voidPtr;
        {
            shared_ptr<Thing> thingPtr = make_shared<Thing>();
            voidPtr = thingPtr;
        }
        cout<<"thingPtr is dead\n";
    }
    cout<<"voidPtr is dead\n";
    return 0;
}

Quais saídas:

thingPtr is dead
Destroyed
voidPtr is dead

Ele se comporta da maneira que eu gosto , mas é totalmente inesperado e eu gostaria de entender o que está acontecendo aqui. O ponteiro compartilhado inicial não existe mais, é apenas um shared_ptr<void> no final. Então, eu esperaria que esse ponteiro compartilhado agisse como se estivesse void* e não tenha idéia sobre Thing::~Thing() , ainda assim o chama. Isso é por design, certo? Como o ponteiro compartilhado nulo está conseguindo isso?


O estado compartilhado em co-propriedade por ponteiros compartilhados também contém um deleter, uma função como objeto que alimenta o objeto gerenciado no final de sua vida útil para liberá-lo. Podemos até especificar nosso próprio deleter usando o construtor apropriado . Como o deleter é armazenado, bem como qualquer tipo de apagamento ao qual é submetido, é um detalhe da implementação. Mas basta dizer que o estado compartilhado contém uma função que sabe exatamente como liberar o recurso de propriedade.

Agora, quando criamos um objeto de um tipo concreto com make_shared<Thing>() e não fornecemos um deleter, o estado compartilhado é definido para conter algum deleter padrão que pode liberar uma Thing . A implementação pode gerar uma somente a partir do argumento do modelo. E, como é armazenado como parte do estado compartilhado, não depende do tipo T de nenhum shared_pointer<T> que possa estar compartilhando a propriedade do estado. Ele sempre saberá como libertar a Thing .

Portanto, mesmo quando voidPtr o voidPtr o único ponteiro restante, o deleter permanece inalterado e ainda sabe como liberar uma Thing . Qual é o que ele faz quando o voidPtr fica fora do escopo.





void