Comment : créer et utiliser des instances weak_ptr
Parfois, un objet doit stocker le moyen d'accéder à l'objet sous-jacent de shared_ptr sans provoquer l'incrémentation du décompte de références. En général, cette situation se produit lorsque vous avez des références cycliques entre des instances shared_ptr.
Le mieux est d'éviter le partage de propriété des pointeurs, chaque fois que cela est possible. Toutefois, si vous avez besoin d'avoir la propriété partagée des instances shared_ptr, évitez les références cycliques entre elles. Lorsque les références cycliques sont inévitables, voire préférables pour une raison quelconque, utilisez weak_ptr pour donner à un ou plusieurs propriétaires une référence faible à un autre shared_ptr. En utilisant un weak_ptr, vous pouvez créer un shared_ptr qui rejoint un ensemble existant d'instances connexes, mais uniquement si la ressource de mémoire sous-jacente est encore valide. Un weak_ptr lui-même ne participe pas au décompte de références. Par conséquent, il ne peut pas empêcher le décompte de références d'atteindre zéro. Toutefois, vous pouvez utiliser weak_ptr pour essayer d'obtenir une nouvelle copie de shared_ptr qui a servi à l'initialiser. Si la mémoire a déjà été supprimée, une exception bad_weak_ptr est levée. Si la mémoire est encore valide, le nouveau pointeur partagé incrémente le décompte de références et garantit que la mémoire sera valide tant que la variable shared_ptr reste dans la portée.
Exemple
L'exemple de code suivant illustre un cas où weak_ptr est utilisé pour garantir la suppression appropriée des objets qui ont des dépendances circulaires. En examinant l'exemple, supposez qu'il a été créé uniquement après la prise en compte de solutions alternatives. Les objets Controller représentent un aspect d'un processus d'ordinateur et fonctionnent indépendamment. Chaque contrôleur doit pouvoir vérifier l'état des autres contrôleurs à tout moment, et chacun contient un vector<weak_ptr<Controller>> privé à cet effet. Chaque vecteur contient une référence circulaire, et les instances de weak_ptr sont donc utilisées au lieu 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);
}
À titre d'expérimentation, remplacez le vecteur others par un vector<shared_ptr<Controller>>, puis dans la sortie, notez qu'aucun destructeur n'est appelé lorsque TestRun est retourné.