Advanced Services | Weakpointer |
Ein weak_ptr ist eine Art abgespeckter shared_ptr und kann nur über einen shared_ptr oder weak_ptr initialisiert werden. Immer wenn man "ownership" nicht auf einen weiteren shared_ptr übertragen will oder kann, kann man einen weak_ptr einsetzen. Ein weak_ptr hat nur eingeschränkte Möglichkeiten, so ist weder operator* operator<< überladen, zudem gibt es keine get()-Methode, es gibt also keinen direkten Zugriff auf die Daten. Die einzige Möglichkeit ist das Erstellen eines neuen shared_ptr aus dem weak_ptr, z.Bsp mit der Methode weak_ptr.lock(). Das erste Beispiel zeigt die grundlegenden Eigenschaften eines weak_ptr.
Wir verwenden die folgende Testklasse.
class Test { public: int i; Test(int j = 0) : i(j) { cout << "Test() i = " << this->i << endl; } ~Test() { cout << "~Test() i = " << this->i << endl << endl; } };
Das Programm
#include <memory> /* Einfache Eigenschaften */ void weakpointer1() { cout << "weakpointer \n" << endl; cout << "shared_ptr 1 anlegen" << endl; // std::shared_ptr<Test> sptr1(new Test(17)); cout << "weak_ptr anlegen" << endl; // std::weak_ptr<Test> wptr(sptr1); //oder //wptr = sptr1; cout << "use_count = " << sptr1.use_count() << endl; // 1 // lock liefert einen shared_ptr und erhöht dadurch den Zähler cout << "shared_ptr 2 mit Hilfe des weak_ptr anlegen" << endl; // auto sptr2 = wptr.lock(); // oder //std::shared_ptr<Test> sptr2(wptr); cout << "use_count = " << wptr.use_count() << endl; // 2 cout << "reset des weak_ptr" << endl; // wptr.reset(); cout << "wptr: use_count = " << wptr.use_count() << endl; // 0 cout << "sptr1: use_count = " << sptr1.use_count() << endl; // 2 cout << "reset des shared_ptr 1" << endl; // sptr1.reset(); cout << "sptr1: use_count = " << sptr1.use_count() << endl; // 0 cout << "sptr2: use_count = " << sptr2.use_count() << endl; // 1 cout << "\nend weakpointer" << endl; }
Die Ausgabe
void weakpointer2() { cout << "weakpointer \n" << endl; std::weak_ptr<Test> wptr; { std::shared_ptr<Test> sptr1; // usecount für beide 0 { cout << "begin block" << endl; sptr1 = std::make_shared<Test>(42); cout << "use_count = " << sptr1.use_count() << endl; // 1 std::shared_ptr<Test> sptr2 = sptr1; // use_count wird erhöht cout << "use_count = " << sptr1.use_count() << endl; // 2 wptr = sptr1; cout << "wptr.use_count = " << wptr.use_count() << endl; // 2 nicht erhöht cout << "end block" << endl; } cout << "wptr.use_count = " << wptr.use_count() << endl; // 1 } cout << "wptr.use_count = " << wptr.use_count() << endl; // cout << "wptr " << (wptr.expired()? "is" : "is not") << " expired\n"; //oder so try { std::shared_ptr<Test> sptr(wptr); } catch(bad_weak_ptr ex) { cout << ex.what() << " cannot create shared_ptr from weak_ptr wptr" << endl; } cout << "\nend weakpointer" << endl; }
Die Ausgabe
Das letzte Beispiel der vorigen Seite behandelte den Fall einer zyklischen Referenz. Mit Hilfe eines weak_ptr können wir die zyklische Referenz aufbrechen. Dazu führen wir in der Klasse B einen weak_ptr ein.
Alte Fassung der beiden Klassen
//cyclic reference struct B; // forward declaration struct A { shared_ptr<B> b; ~A() {cout << "~A()" << endl; } }; struct B { shared_ptr<A> a; ~B() {cout << "~B()" << endl;} };
Neue Fassung der beiden Klassen (die erste Klasse bleibt unverändert)
//weak cyclic struct BB; // forward declaration struct AA { shared_ptr<BB> bb; ~AA() {cout << "~AA()" << endl; } }; struct BB { weak_ptr<AA> aa; ~BB() {cout << "~BB()" << endl;} };
Das Programm
/* Problem behoben : In BB ist nun ein weakpointer ! */ void cyclicsharedpointer2() { cout << "begin cyclicsharedpointer 2\n" << endl; // shared_ptr<AA> shptrAA(new AA); // shptrAA->b share_ptr Initialisierung mit 0 cout << "shptrAA.use_count = " << shptrAA.use_count() << endl; // 1 cout << "shptrAA->b.use_count = " << shptrAA->bb.use_count() << endl; // 0 shptrAA->bb = make_shared<BB>(); // zweiter shared_ptr; cout << "shptrAA->b.use_count = " << shptrAA->bb.use_count() << endl; // shptrAA->bb->aa = shptrAA; cout << "shptrAA.use_count = " << shptrAA.use_count() << endl; // 2 cout << "\nend cyclicsharedpointer 2" << endl; // }
Die Ausgabe
Im vorigen Beispiel fehlt noch die Verwendung des weak_ptr. Im nächsten Beispiel aktivieren wir über den weak_ptr einen shared_ptr und manipulieren die Daten des Objekts auf den der weak_ptr zeigt. Das geschieht problemlos in der Methode dosomething().
Die um einen Datenteil ergänzte Klasse AA
struct AA { int i; shared_ptr<BB> bb; ~AA() {cout << "~AA()" << endl; } };
Die um eine Methode erweiterte Klasse BB
struct BB { weak_ptr<AA> aa; void dosomething() { cout << "dosomething" << endl; shared_ptr<AA> sha = aa.lock(); sha->i = 42; } ~BB() {cout << "~BB()" << endl;} };
Das Pprogramm
void cyclicsharedpointer3() { cout << "begin cyclicsharedpointer 3\n" << endl; // shared_ptr<AA> shptrAA(new AA); // shptrAA->b share_ptr Initialisierung mit 0 cout << "shptrAA.use_count = " << shptrAA.use_count() << endl; // 1 cout << "shptrAA->b.use_count = " << shptrAA->bb.use_count() << endl; // 0 shptrAA->bb = make_shared<BB>(); // zweiter shared_ptr; cout << "shptrAA->b.use_count = " << shptrAA->bb.use_count() << endl; // 1 shptrAA->bb->aa = shptrAA; cout << "shptrAA.use_count = " << shptrAA.use_count() << endl; // 2 // Verwenden des weak_ptr als shred_ptr (shptrAA->bb)->dosomething(); cout << "shptrAA->i = " << shptrAA->i << endl; cout << "\nend cyclicsharedpointer 3" << endl; // }
Die Ausgabe