Advanced Services | Intrusivepointer (boost) |
Link zur API www.boost.org/doc/libs/1_54_0/libs/smart_ptr/intrusive_ptr.html
intrusive_ptr ist ein weiterer interessanter Pointer aus der boost Library. Wie bei shared_ptr gibt es einen Zähler, der die Anzahl der Referenzen zählt. Wenn der Zähler auf 0 sinkt wird wird der Destruktor gerufen. Allerdings befindet sich dieser Zähler hier im referenzierten Objekt selbst. Es muß also Hilfsfunktionen geben, die zum Objekt bereitgestellt werden. Diese Hilfsfunktionen haben eine vorgeschriebene Signatur und müssen in der Klasse als friend-Funktionen deklariert werden. Damit mehrere intrusive_ptr auf das gleiche Objekt zeigen könmnen gibt es einen Konstruktor der Form intrusive_ptr(intrusive_ptr const & r).
Das folgende Beispiel erläutert das Vorgehen. Zuerst die Testklasse.
class Test { private: long refcount; std::string name; public: Test(string name) : refcount(0), name(name) {} ~Test() { cout << "destruktor " << this->name << endl; } string getName() { return this->name; } int getRefcount() { return refcount; } friend void intrusive_ptr_add_ref(Test * p); friend void intrusive_ptr_release(Test * p); };
Es biete sich an, die Hilfsfunktion inline zu codieren.
// friends inline void intrusive_ptr_add_ref(Test* x) { cout << "intrusive_ptr_add_ref" << endl; ++x->refcount; }; inline void intrusive_ptr_release(Test* x) { cout << "intrusive_ptr_release" << endl; if(--x->refcount == 0) delete x; };
Das Beispiel legt drei intrusive_ptr in zwei geschachtelten Blöcken an.
void intrusiveptr1() { boost::intrusive_ptr<Test> intrPtr1( new Test("foo") ); cout << intrPtr1->getName() << " : refcount = " << intrPtr1->getRefcount() << std::endl; { cout << " begin block" << endl << " "; boost::intrusive_ptr<Test> intrPtr2( new Test("bar") ); cout << " " << (*intrPtr2).getName() << " : refcount = " << (*intrPtr2).getRefcount() << endl << " "; boost::intrusive_ptr<Test> intrPtr3(intrPtr1); cout << " " << intrPtr1->getName() << " : refcount = " << intrPtr1->getRefcount() << std::endl; cout << " end block" << endl; } cout << intrPtr1->getName() << " : refcount = " << intrPtr1->getRefcount() << std::endl; }
Die Ausgabe
Wie man im oberen Beispiel sieht, sind operator* und operator-> überladen, auch gibt es eine Methode T * get() const die den originalen raw-Pointer zurückliefert. Diese drei Funktionen finden sich bei allen Smartpointern außer bei weak_ptr.
swap() vertauscht den Inhalt zweier intrusive_ptr. Das folgende Beispiel zeigt dies.
void intrusiveptr2() { boost::intrusive_ptr<Test> intrPtr1( new Test("ptr1") ); boost::intrusive_ptr<Test> intrPtr2( new Test("ptr2") ); boost::intrusive_ptr<Test> intrPtr3(intrPtr2); cout << intrPtr1->getName() << " : refcount = " << intrPtr1->getRefcount() << endl; cout << intrPtr2->getName() << " : refcount = " << intrPtr2->getRefcount() << endl; cout << intrPtr3->getName() << " : refcount = " << intrPtr3->getRefcount() << endl; intrPtr2.swap(intrPtr1); cout << "nach swap:" << endl; cout << intrPtr1->getName() << " : refcount = " << intrPtr1->getRefcount() << endl; cout << intrPtr2->getName() << " : refcount = " << intrPtr2->getRefcount() << endl; cout << intrPtr3->getName() << " : refcount = " << intrPtr3->getRefcount() << endl; }
Die Ausgabe
Die Wirkung von reset() erkennt man am nächsten Beispiel.
void intrusiveptr3() { boost::intrusive_ptr<Test> intrPtr1( new Test("foo") ); cout << intrPtr1->getName() << " : refcount = " << intrPtr1->getRefcount(); cout << " address = " << intrPtr1.get() << endl; cout << "reset intrusive_ptr" << endl; intrPtr1.reset(); cout << "address = " << intrPtr1.get() << endl; }
reset() gibt es in zwei Varianten. Aus der API kann man entnehmen, wie die Funktionen implementiert sind:
void reset() ist äquivalent zu intrusive_ptr().swap(*this).
void reset(T* rp) ist äquivalent zu intrusive_ptr(rp).swap(*this).
Ähnlich wie bei shared_ptr sind auch bei intrusive_ptr einige Vergleichsfunktionen überladen. Aus der API sieht man, daß alle diese Vergleiche über die Memberfunktion get() auf einen Vergleich der raw-Pointer hinauslaufen. == und != sind mehrfach überladen, sodaß beide Operanden auch vom Typ T* des raw-Pointers sein können.
void intrusiveptr4() { boost::intrusive_ptr<Test> intrPtr1( new Test("1") ); boost::intrusive_ptr<Test> intrPtr2( new Test("2") ); boost::intrusive_ptr<Test> intrPtr3(intrPtr2); cout << "address1 = " << intrPtr1.get() << endl; cout << "address2 = " << intrPtr2.get() << endl; cout << "address3 = " << intrPtr3.get() << endl; cout << "(intrPtr1 == intrPtr2) = " << (intrPtr1 == intrPtr2) << endl; // false cout << "(intrPtr2 == intrPtr2) = " << (intrPtr2 == intrPtr3) << endl; // true cout << "(intrPtr1 < intrPtr3) = " << (intrPtr1 < intrPtr3) << endl; // true }
Für die Ausgabe wurden die die Konsolausgaben in den Hilfsfunktionen auskommentiert.
Wir übernehmen den folgenden Satz aus der API
As a general rule, if it isn't obvious whether intrusive_ptr better fits your needs than shared_ptr, try a shared_ptr-based design first.
Da shared_ptr mit einem internen Referenzuzähler arbeiten entfallen die externen Referenzzähler, es brauchen also bestehende Klassen nicht verändert werden, dadurch ist der Einsatz von shared_ptr einfacher. Zeigen mehrere shared_ptr auf dasselbe Objekt kann es aber vorkommen, daß zwei shared_ptr unterschiedliche Referenzzahlen haben, da der Counter im shared_ptr gespeichert wird, was beim Löschen der Objekte zu Problemen führen kann. Zeigen dagegen mehrere intrusive_ptr auf dasselbe Objekt, so haben sie immer dieselbe Referenzzahl, da der Counter im Objekt liegt.