Alle in diesem Kapitel kennengelernten Smartpointer können für sich allein genommen in unterschiedlichen Situationen verwendet werden. Der Smartpointer vom Typ boost::weak_ptr
ergibt jedoch nur im Zusammenspiel mit boost::shared_ptr
Sinn. Er ist in der Headerdatei boost/weak_ptr.hpp
definiert.
boost::weak_ptr
in Aktion#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <thread>
#include <functional>
#include <iostream>
void reset(boost::shared_ptr<int> &sh)
{
sh.reset();
}
void print(boost::weak_ptr<int> &w)
{
boost::shared_ptr<int> sh = w.lock();
if (sh)
std::cout << *sh << '\n';
}
int main()
{
boost::shared_ptr<int> sh{new int{99}};
boost::weak_ptr<int> w{sh};
std::thread t1{reset, std::ref(sh)};
std::thread t2{print, std::ref(w)};
t1.join();
t2.join();
}
Die Klasse boost::weak_ptr
muss mit einem boost::shared_ptr
initialisiert werden. Die wichtigste von ihr angebotene Methode ist lock()
. lock()
gibt einen boost::shared_ptr
zurück, der das Eigentum mit dem Smartpointer teilt, mit dem boost::weak_ptr
initialisiert wurde. Für den Fall, dass der boost::shared_ptr
, der bei der Initialisierung angegeben wurde, nicht mehr auf ein Objekt zeigt, ist der von lock()
zurückgegebene boost::shared_ptr
leer.
boost::weak_ptr
bietet sich an, wenn eine Funktion mit einem in einem boost::shared_ptr
verwalteten Objekt arbeiten soll, die Lebensdauer dieses Objekts jedoch nicht von dieser Funktion abhängen soll. Die Funktion soll nur dann mit dem Objekt arbeiten, wenn es momentan in Smartpointern vom Typ boost::shared_ptr
, die an anderen Stellen im Programm existieren, verankert ist. Verschwinden diese Smartpointer, soll das entsprechende Objekt nicht mehr existieren und nicht unnötigerweise durch einen zusätzlichen Smartpointer vom Typ boost::shared_ptr
in der entsprechenden Funktion am Leben gehalten werden.
Im Beispiel 1.8 werden in main()
zwei Threads erstellt. Ein Thread führt die Funktion reset()
aus, die eine Referenz auf einen boost::shared_ptr
erhält. Der andere Thread besteht aus der Funktion print()
, die eine Referenz auf einen boost::weak_ptr
erhält. Dieser boost::weak_ptr
wird in der Funktion main()
mit dem boost::shared_ptr
initialisiert.
Wenn das Programm gestartet wird, werden die beiden Funktionen reset()
und print()
gleichzeitig in zwei Threads ausgeführt. Es lässt sich nicht vorhersagen, in welcher Reihenfolge die verschiedenen Anweisungen in den Threads abgearbeitet werden. Das Problem ist, dass in der Funktion reset()
ein Objekt zerstört werden soll, das gleichzeitig in einem anderen Thread verarbeitet und auf die Standardausgabe ausgegeben werden soll. Es könnte sein, dass in der Funktion reset()
das Objekt zerstört wird, während in der Funktion print()
auf dieses Objekt zugegriffen wird, um es auszugeben.
boost::weak_ptr
löst diese Problematik wie folgt: Beim Aufruf von lock()
wird ein boost::shared_ptr
zurückgeben. Dieser boost::shared_ptr
zeigt auf ein Objekt, wenn dieses Objekt zum Zeitpunkt des Aufrufs existiert. Andernfalls ist der boost::shared_ptr
leer und ein Null-Zeiger.
boost::weak_ptr
allein hat keinen Einfluss auf die Lebensdauer eines Objekts. Damit in print()
gefahrlos auf das Objekt zugegriffen kann, ohne dass es in einem anderen Thread zerstört wird, gibt lock()
einen boost::shared_ptr
zurück. Wird in einem anderen Thread versucht, das Objekt zu zerstören, existiert es dank des von lock()
zurückgegebenen Smartpointers vom Typ boost::shared_ptr
weiter.
boost::intrusive_ptr
in Aktion#include <boost/intrusive_ptr.hpp>
#include <atlbase.h>
#include <iostream>
void intrusive_ptr_add_ref(IDispatch *p) { p->AddRef(); }
void intrusive_ptr_release(IDispatch *p) { p->Release(); }
void check_windows_folder()
{
CLSID clsid;
CLSIDFromProgID(CComBSTR{"Scripting.FileSystemObject"}, &clsid);
void *p;
CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, __uuidof(IDispatch), &p);
boost::intrusive_ptr<IDispatch> disp{static_cast<IDispatch*>(p), false};
CComDispatchDriver dd{disp.get()};
CComVariant arg{"C:\\Windows"};
CComVariant ret{false};
dd.Invoke1(CComBSTR{"FolderExists"}, &arg, &ret);
std::cout << std::boolalpha << (ret.boolVal != 0) << '\n';
}
int main()
{
CoInitialize(0);
check_windows_folder();
CoUninitialize();
}
Der Smartpointer boost::intrusive_ptr
funktioniert genauso wie boost::shared_ptr
. Während boost::shared_ptr
jedoch automatisch mitzählt, wie viele Kopien momentan auf ein Objekt verweisen und sich das Eigentum teilen, müssen bei boost::intrusive_ptr
Sie mitzählen. Das ist zum Beispiel von Vorteil, wenn auf Klassen aus Frameworks zugegriffen wird, die sowieso schon mitzählen.
boost::intrusive_ptr
ist in der Headerdatei boost/intrusive_ptr.hpp
definiert.
Im Beispiel 1.9 wird auf Microsoft COM-Funktionen zugegriffen – das Beispiel kann ausschließlich unter Windows ausgeführt werden. COM-Objekte bieten sich als Beispiel für boost::intrusive_ptr
an, da sie mitzählen, wie viele Zeiger momentan auf sie verweisen. Der interne Referenzzähler eines COM-Objekts kann über die Methoden AddRef()
und Release()
jeweils um 1 erhöht und verringert werden. Wird der interne Zähler nach einem Aufruf von Release()
auf 0 gesetzt, wird das COM-Objekt automatisch zerstört.
Die beiden Methoden AddRef()
und Release()
werden innerhalb der Funktionen intrusive_ptr_add_ref()
und intrusive_ptr_release()
aufgerufen, um den internen Zähler im COM-Objekt zu inkrementieren und dekrementieren. Boost.SmartPointers erwartet, dass Sie diese beiden Funktionen zur Verfügung stellen. Sie werden automatisch aufgerufen, wenn der interne Zähler inkrementiert oder dekrementiert werden muss. Der Parameter, der an diese Funktionen übergeben wird, ist ein Zeiger auf den Typ, mit dem das Template boost::intrusive_ptr
instanziiert wurde.
Das COM-Objekt, das im Beispiel 1.9 verwendet wird, heißt FileSystemObject und ist standardmäßig unter Windows vorhanden. Es gestattet einen Zugriff aufs Dateisystem, um zum Beispiel zu überprüfen, ob ein bestimmtes Verzeichnis existiert. Im Beispiel wird überprüft, ob es das Verzeichnis C:\Windows
gibt. Wie dies im Detail funktioniert, hängt von COM ab und ist unwichtig, um die Funktionsweise von boost::intrusive_ptr
zu verstehen. Entscheidend ist, dass am Ende der Funktion check_windows_folder()
, wenn der Gültigkeitsbereich von disp endet, die Funktion intrusive_ptr_release()
automatisch aufgerufen wird. Nachdem dort mit Release()
der interne Zähler auf 0 gesetzt wird, wird das COM-Objekt FileSystemObject zerstört.
Der Parameter false
, der an den Konstruktor von boost::intrusive_ptr
übergeben wird, verhindert, dass intrusive_ptr_add_ref()
aufgerufen wird. Da ein COM-Objekt, wenn es mit CoCreateInstance()
erstellt wird, einen bereits auf 1 gesetzten Referenzzähler hat, darf dieser nicht noch einmal in intrusive_ptr_add_ref()
inkrementiert werden.