Compartilhar via


Como criar e usar instâncias weak_ptr

Às vezes um objeto deve armazenar uma maneira de acessar o objeto subjacente de um shared_ptr sem fazer com que a contagem de referência seja incrementada. Normalmente, essa situação ocorre quando você tem referências cíclicas entre instâncias de shared_ptr.

O melhor design é impedir a propriedade compartilhada de ponteiros sempre que possível. No entanto, se você precisa ter a propriedade compartilhada de instâncias shared_ptr, evite referências cíclicas entre elas. Quando as referências cíclicas forem inevitáveis, ou mesmo preferíveis por algum motivo, use weak_ptr para dar a um ou mais dos proprietários uma referência fraca para outro shared_ptr. Usando weak_ptr, você pode criar shared_ptr que associa a um conjunto existente de instâncias relacionadas, mas somente se o recurso de memória subjacente ainda for válido. Um weak_ptr em si não participa da contagem de referência e, portanto, não pode impedir a contagem de referência de zerar. No entanto, você pode usar um weak_ptr para tentar obter uma nova cópia do shared_ptr com o qual foi inicializado. Se a memória já foi excluída, uma exceção bad_weak_ptr é lançada. Se a memória ainda é válida, o novo ponteiro compartilhado incrementa a contagem de referência e garante que a memória seja válida contanto que a variável shared_ptr esteja no escopo.

Exemplo

O exemplo de código a seguir mostra um caso onde weak_ptr é usado para garantir a exclusão apropriada de objetos que possuem dependências circulares. À medida que você examina o exemplo, suponha que foi criado somente após as soluções alternativas terem sidos consideradas. Os objetos Controller representam algum aspecto de um processo do computador e operam de forma independente. Cada controlador deve ser capaz de consultar o status dos outros controladores a qualquer momento, e cada um contém um vector<weak_ptr<Controller>> específico para essa finalidade. Cada vetor contém uma referência circular e, portanto, as instâncias de weak_ptr são usadas em vez de shared_ptr.

#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

class Controller 
{
public:
    int Num;
    wstring Status;
    vector<weak_ptr<Controller>> others;
    explicit Controller(int i) : Num(i) , Status(L"On")
    {
        wcout << L"Creating Controller" << Num << endl;
    }

    ~Controller()
    {
        wcout << L"Destroying Controller" << Num << endl;
    }

    // Demonstrates how to test whether the   
    // pointed-to memory still exists or not.  
    void CheckStatuses() const
    {
        for_each(others.begin(), others.end(), [] (weak_ptr<Controller> wp)
        {
            try
            {
                auto p = wp.lock();
                wcout << L"Status of " << p->Num << " = " << p->Status << endl;
            }

            catch (bad_weak_ptr b)
            {
                wcout << L"Null object" << endl;
            }                
        });
    }
};

void RunTest()
{
    vector<shared_ptr<Controller>> v;

    v.push_back(shared_ptr<Controller>(new Controller(0)));
    v.push_back(shared_ptr<Controller>(new Controller(1)));
    v.push_back(shared_ptr<Controller>(new Controller(2)));
    v.push_back(shared_ptr<Controller>(new Controller(3)));
    v.push_back(shared_ptr<Controller>(new Controller(4)));

    // Each controller depends on all others not being deleted.  
    // Give each controller a pointer to all the others.  
    for (int i = 0 ; i < v.size(); ++i)
    {
        for_each(v.begin(), v.end(), [v,i] (shared_ptr<Controller> p)
        {
            if(p->Num != i)
            {
                v[i]->others.push_back(weak_ptr<Controller>(p));
                wcout << L"push_back to v[" << i << "]: " << p->Num << endl;
            }
        });        
    }

    for_each(v.begin(), v.end(), [](shared_ptr<Controller>& p)
    {
        wcout << L"use_count = " << p.use_count() << endl;
        p->CheckStatuses();
    });
}

int main()
{    
    RunTest(); 
    wcout << L"Press any key" << endl;
    char ch;
    cin.getline(&ch, 1);
}
  

Como experiência, modifique o vetor others para ser vector<shared_ptr<Controller>> e, na saída, observe que nenhum destruidor é invocado quando TestRun retorna.

Consulte também

Conceitos

Ponteiros inteligentes (C++ moderno)