Procedura: creare e utilizzare istanze weak_ptr
Talvolta un oggetto deve memorizzare un modo per accedere all'oggetto sottostante di un shared_ptr senza causare l'incremento del conteggio dei riferimenti. In genere, questa situazione si verifica in presenza di riferimenti ciclici tra le istanze di shared_ptr.
Per progettare al meglio è consigliato evitare il più possibile la proprietà condivisa dei puntatori. Tuttavia, se è necessario condividere la proprietà di istanze shared_ptr, evitare riferimenti ciclici tra queste. Quando i riferimenti ciclici sono inevitabili o preferibili per una qualche ragione, utilizzare weak_ptr per fornire a uno o più proprietari un riferimento debole a un altro shared_ptr. Utilizzando weak_ptr, è possibile creare un oggetto shared_ptr che si unisce a un set di istanze correlate esistente, ma solo se la risorsa di memoria sottostante è ancora valida. weak_ptr non partecipa al conteggio dei riferimenti, pertanto non può impedire che il conteggio dei riferimenti passi a zero. Tuttavia, è possibile utilizzare weak_ptr per tentare di ottenere una nuova copia di shared_ptr con cui è stata inizializzata. Se la memoria è già stata eliminata, viene generata un'eccezione bad_weak_ptr. Se la memoria è ancora valida, il nuovo puntatore condiviso incrementa il conteggio dei riferimenti e garantisce che la memoria sarà valida fino a che la variabile shared_ptr rimane nell'ambito.
Esempio
Nell'esempio di codice seguente è illustrato un caso in cui weak_ptr è utilizzato per garantire l'eliminazione di oggetti con dipendenze circolari. Quando si esamina l'esempio, si supponga che è stato creato solo dopo che le soluzioni alternative sono state valutate. Gli oggetti Controller rappresentano alcuni aspetti del processo di un computer e operano in modo indipendente. Ogni controller deve essere in grado di eseguire una query sullo stato degli altri controller in qualsiasi momento e contiene un vector<weak_ptr<Controller>> privato per espletare questa funzione. Ogni vettore contiene un riferimento circolare, pertanto vengono utilizzate le istanze weak_ptr anziché 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);
}
Come esperimento, modificare il vettore others in vector<shared_ptr<Controller>>, quindi nell'output, notare che non viene richiamato alcun distruttore quando viene restituito TestRun.