Der Smartpointer boost::shared_ptr
ähnelt boost::scoped_ptr
. Der entscheidende Unterschied ist, dass boost::shared_ptr
nicht exklusiver Eigentümer eines Objekts ist, sondern mit anderen Smartpointern vom Typ boost::shared_ptr
das Eigentum teilen kann. Das Objekt, dessen Adresse in mehreren Kopien eines Smartpointers vom Typ boost::shared_ptr
gespeichert ist, wird erst dann zerstört, wenn die letzte Kopie zerstört wird. Da boost::shared_ptr
das Eigentum teilen kann, können Kopien dieses Smartpointers erstellt werden – etwas, was mit boost::scoped_ptr
nicht möglich ist.
boost::shared_ptr
ist in der Headerdatei boost/shared_ptr.hpp
definiert.
boost::shared_ptr
in Aktion#include <boost/shared_ptr.hpp>
#include <iostream>
int main()
{
boost::shared_ptr<int> p1{new int{1}};
std::cout << *p1 << '\n';
boost::shared_ptr<int> p2{p1};
p1.reset(new int{2});
std::cout << *p1.get() << '\n';
p1.reset();
std::cout << std::boolalpha << static_cast<bool>(p2) << '\n';
}
Im Beispiel 1.3 werden zwei Smartpointer p1 und p2 vom Typ boost::shared_ptr
verwendet. p2 wird mit p1 initialisiert, woraufhin sich beide Smartpointer das Eigentum an der int
-Variablen mit dem Wert 1 teilen. Wenn für p1 reset()
aufgerufen wird, wird eine neue int
-Variable in p1 verankert. Dies bedeutet nicht, dass die bereits existierende int
-Variable zerstört wird. Da diese auch in p2 verankert ist, existiert sie weiterhin. Nach dem ersten Aufruf von reset()
ist p1 alleiniger Eigentümer einer int
-Variablen mit dem Wert 2 und p2 alleiniger Eigentümer einer int
-Variablen mit dem Wert 1.
boost::shared_ptr
verwendet intern einen Referenzzähler. Erst wenn boost::shared_ptr
feststellt, dass die letzte Kopie eines Smartpointers zerstört wird, wird das entsprechende Objekt mit delete
freigegeben.
boost::shared_ptr
überlädt ähnlich wie boost::scoped_ptr
die Operatoren operator*()
, operator->()
und operator bool()
. Darüberhinaus stehen die Methoden get()
und reset()
zur Verfügung, um die gespeicherte Adresse abzurufen und eine neue Adresse zu setzen.
Dem Konstruktor von boost::shared_ptr
kann als zweiter Parameter ein Deleter übergeben werden. Dabei muss es sich um eine Funktion oder ein Funktionsobjekt handeln, das als einzigen Parameter einen Zeiger auf den Typ erwartet, mit dem boost::shared_ptr
instanziiert ist. Der Deleter wird im Destruktor von boost::shared_ptr
anstelle von delete
verwendet. So ist es möglich, andere Ressourcen als dynamisch reservierte Objekte in einem boost::shared_ptr
zu verwalten.
boost::shared_ptr
mit benutzerdefiniertem Deleter#include <boost/shared_ptr.hpp>
#include <Windows.h>
int main()
{
boost::shared_ptr<void> handle(OpenProcess(PROCESS_SET_INFORMATION, FALSE,
GetCurrentProcessId()), CloseHandle);
}
Im Beispiel 1.4 wird boost::shared_ptr
mit dem Typ void
instanziiert. Als ersten Parameter wird dem Konstruktor der Rückgabewert von OpenProcess()
übergeben. Es handelt sich hierbei um eine Windows-Funktion, um einen Handle auf einen Prozess zu erhalten. Über den Aufruf von OpenProcess()
wird im Beispiel ein Handle auf den eigenen Prozess erhalten – auf das Beispiel selber.
Handle stellen unter Windows Verweise auf Ressourcen dar, die geschlossen werden müssen, wenn sie nicht mehr benötigt werden. Dazu stellt Windows die Funktion CloseHandle()
zur Verfügung. Dieser muss als einziger Parameter der Handle für die Ressource übergeben werden, die geschlossen werden soll. Im Beispiel wird CloseHandle()
als zweiter Parameter an den Konstruktor von boost::shared_ptr
übergeben. CloseHandle()
stellt den Deleter von handle dar. Wenn handle am Ende von main()
zerstört wird, greift der Destruktor auf CloseHandle()
zu und gibt die Ressource frei, deren Handle als erster Parameter an den Konstruktor übergeben wurde.
Beachten Sie, dass das Beispiel nur deswegen funktioniert, weil ein Handle als void*
definiert ist. Würde OpenProcess()
nicht den Rückgabewert void*
haben und CloseHandle()
keinen Parameter vom Typ void*
erwarten, könnte boost::shared_ptr
nicht mit void
instanziiert werden. boost::shared_ptr
ist kein Allheilmittel, das sich dank Deleter zur Verwaltung beliebiger Ressourcen eignet.
boost::make_shared
in Aktion#include <boost/make_shared.hpp>
#include <typeinfo>
#include <iostream>
int main()
{
auto p1 = boost::make_shared<int>(1);
std::cout << typeid(p1).name() << '\n';
auto p2 = boost::make_shared<int[]>(10);
std::cout << typeid(p2).name() << '\n';
}
Boost.SmartPointers bietet in der Headerdatei boost/make_shared.hpp
eine Hilfsfunktion boost::make_shared()
an. Sie können mit boost::make_shared()
Smartpointer vom Typ boost::shared_ptr
erstellen, ohne auf diese Klasse direkt zugreifen und den Konstruktor aufrufen zu müssen.
boost::make_shared()
hat den Vorteil, dass der Speicher für das dynamisch zu reservierende Objekt und für den vom Smartpointer verwendeten Referenzzähler in einem Block reserviert werden kann. boost::make_shared()
ist effizienter, als wenn Sie das dynamisch zu reservierende Objekt mit new
erstellen und der Konstruktor von boost::shared_ptr
ein weiteres Mal auf new
zugreift, um Speicher für den Referenzzähler zu reservieren.
Sie können boost::make_shared()
auch für Arrays verwenden. So führt der zweite Aufruf von boost::make_shared()
im Beispiel 1.5 dazu, dass in p2 ein int
-Array mit zehn Elementen verankert wird.
Beachten Sie, dass es erst seit Boost 1.53.0 möglich ist, boost::shared_ptr
für Arrays zu verwenden. Boost.SmartPointers bietet mit boost::shared_array
einen Smartpointer an, der sich zu boost::shared_ptr
verhält wie boost::scoped_array
zu boost::scoped_ptr
. Wenn Sie obiges Beispiel mit Visual C++ 2013 und Boost 1.53.0 oder neuer ausführen, wird für p2 class boost::shared_ptr<int [0]>
ausgegeben. Seit Boost 1.53.0 unterstützt boost::shared_ptr
sowohl einzelne Objekte als auch Arrays und erkennt, ob die Freigabe im Destruktor mit delete
oder delete[]
erfolgen muss. Da boost::shared_ptr
seit Boost 1.53.0 außerdem den Operator operator[]
anbietet, stellt dieser Smartpointer eine Alternative zu boost::shared_array
dar.
boost::shared_array
in Aktion#include <boost/shared_array.hpp>
#include <iostream>
int main()
{
boost::shared_array<int> p1{new int[1]};
{
boost::shared_array<int> p2{p1};
p2[0] = 1;
}
std::cout << p1[0] << '\n';
}
boost::shared_array
ergänzt boost::shared_ptr
: Da boost::shared_array
im Destruktor delete[]
ausführt, kann dieser Smartpointer für dynamisch reservierte Arrays verwendet werden. Vor Boost 1.53.0 musste boost::shared_array
für Arrays verwendet werden – boost::shared_ptr
unterstützte Arrays nicht.
boost::shared_array
ist in der Headerdatei boost/shared_array.hpp
definiert.
Im Beispiel 1.6 teilen sich die Smartpointer p1 und p2 das Eigentum an einem dynamisch reservierten int
-Array. Wenn über operator[]
auf p2 zugegriffen wird, um den Wert 1 im Array zu speichern, kann über p1 auf das gleiche dynamisch reservierte Array zugegriffen werden. Das Beispiel gibt entsprechend 1
auf die Standardausgabe aus.
boost::shared_array
verwendet wie boost::shared_ptr
einen Referenzzähler. Das dynamisch reservierte Array wird nicht freigegeben, wenn der Gültigkeitsbereich von p2 endet. Zu diesem Zeitpunkt existiert noch eine Referenz von p1 auf das Array. Erst am Ende von main()
, wenn der Gültigkeitsbereich von p1 endet, wird das Array zerstört.
boost::shared_array
bietet ebenfalls die Methoden get()
und reset()
an. Außerdem ist der Operator operator bool
überladen.
boost::shared_ptr
mit BOOST_SP_USE_QUICK_ALLOCATOR
#define BOOST_SP_USE_QUICK_ALLOCATOR
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <ctime>
int main()
{
boost::shared_ptr<int> p;
std::time_t then = std::time(nullptr);
for (int i = 0; i < 1000000; ++i)
p.reset(new int{i});
std::time_t now = std::time(nullptr);
std::cout << now - then << '\n';
}
Beachten Sie, dass es von Vorteil sein kann, Smartpointer wie boost::shared_ptr
denen aus der Standardbibliothek vorzuziehen. Boost.SmartPointers bietet Makros an, um das Verhalten der Smartpointer zu optimieren. So wird im Beispiel 1.7 das Makro BOOST_SP_USE_QUICK_ALLOCATOR
verwendet, um einen in Boost.SmartPointers integrierten Allokator zu aktivieren. Der Allokator reserviert und verwaltet Speicherblöcke, um die Anzahl der Aufrufe von new
und delete
zu minimieren, die zur Allokation von Referenzzählern benötigt werden. Im Beispiel wird mit std::time()
die Zeit vor und nach der Schleife gemessen. Während die gemessene Ausführungsgeschwindigkeit vom verwendeten Computer abhängt, kann das Programm mit BOOST_SP_USE_QUICK_ALLOCATOR
schneller sein als ohne. BOOST_SP_USE_QUICK_ALLOCATOR
wird in der Dokumentation der Boost-Bibliothek nicht erwähnt. Sie sollten daher die Ausführungsgeschwindigkeit Ihres Programms ausführlich messen und die Ergebnisse vergleichen, die Sie mit und ohne BOOST_SP_USE_QUICK_ALLOCATOR
erhalten.
Neben BOOST_SP_USE_QUICK_ALLOCATOR
unterstützt Boost.SmartPointers weitere Makros wie zum Beispiel BOOST_SP_ENABLE_DEBUG_HOOKS
. Die Namen der Makros beginnen mit BOOST_SP_, so dass eine Suche in den Headerdateien von Boost.SmartPointers einen raschen Überblick über die unterstützten Makros verschafft.