1. Kurs: Boost C++-Bibliotheken (http://tutego.de/g/BOOSTCPP/)
Autor: Boris Schäling
Kapitel 2: Smart Pointers
2.1 Allgemeines
In der 1998 verabschiedeten ersten Version des C++ Standards gibt es lediglich einen smart
pointer: std::auto_ptr. Dieser verhält sich grundsätzlich wie ein herkömmlicher Zeiger: Er
speichert eine Adresse, über die auf ein dynamisch reserviertes Objekt zugegriffen werden
kann. Der Zeiger std::auto_ptr wird jedoch als intelligent bzw. smart bezeichnet, weil er
automatisch im Destruktor das Objekt mit delete freigibt, dessen Adresse er speichert. Das
setzt voraus, dass er mit der Adresse eines Objekts initialisiert wird, das mit new erstellt
wurde. Der Vorteil aber ist, dass die Speicherfreigabe mit delete nicht vergessen werden
kann, da sie immer garantiert im Destruktor von std::auto_ptr erfolgt. Dies ist vor allem
im Zusammenhang mit Exceptions wichtig: Ohne smart pointers wie std::auto_ptr wäre es
notwendig, in jeder Funktion, die dynamisch Speicher reserviert, mögliche Ausnahmen
abzufangen, um dann den dynamisch reservierten Speicher freizugeben, bevor die
Ausnahme wieder geworfen und an die aufrufende Funktion weitergereicht wird. Die Boost
C++ Bibliothek Smart Pointers bietet nun zahlreiche weitere smart pointer an, die in
unterschiedlichen Situationen eingesetzt werden können.
2.2 RAII
Smart pointers basieren auf einem Prinzip namens RAII: Resource Acquisition Is Initialization.
Smart pointers sind nur ein Beispiel für RAII, wenn auch ein sehr prominentes. Smart
pointers werden verwendet, um dynamisch reservierten Speicher garantiert wieder
freizugeben - ohne dass ein Entwickler die Freigabe vergessen kann oder die Freigabe wegen
einer Ausnahme nicht ausgeführt wird, weil die Ausführung einer Funktion abgebrochen
wird und die Anweisung zur Speicherfreigabe übersprungen wird. Diese Garantie wird
dadurch erreicht, indem ein smart pointer mit der Adresse eines dynamisch reservierten
Objekts initialisiert wird und im Destruktor die Adresse zur Speicherfreigabe verwendet. Da
der Destruktor immer ausgeführt wird, erfolgt demnach auch immer eine Speicherfreigabe.
RAII wird immer dann angewandt, wenn auf einen Schritt zwingend ein zweiter Schritt
erfolgen muss, um eine im ersten Schritt geöffnete Resource wieder zu schließen. Da in sehr
vielen C++-Programmen Speicher dynamisch verwaltet werden muss, sind smart pointers
wichtige RAII-Klassen. RAII lässt sich aber durchaus auch in anderen Fällen verwenden.
#include <windows.h>
class windows_handle
{
public:
windows_handle(HANDLE h)
: handle_(h)
{
}
~windows_handle()
2. {
CloseHandle(handle_);
}
HANDLE handle() const
{
return handle_;
}
private:
HANDLE handle_;
};
int main()
{
windows_handle h(OpenProcess(PROCESS_SET_INFORMATION, FALSE,
GetCurrentProcessId()));
SetPriorityClass(h.handle(), HIGH_PRIORITY_CLASS);
}
Im obigen Beispiel wird eine Klasse windows_handle definiert, die im Destruktor die
Funktion CloseHandle() aufruft. Es handelt sich dabei um eine Windows API-Funktion, so
dass das obige Programm lediglich unter Windows läuft. Die Klasse windows_handle ist
unter Windows insofern nützlich als dass viele Ressourcen unter Windows zuerst geöffnet
werden müssen, bevor sie verwendet werden können. Das Öffnen setzt voraus, dass
Ressourcen, wenn sie nicht mehr benötigt werden, geschlossen werden. Die Klasse
windows_handle soll nun sicherstellen, dass Ressourcen garantiert geschlossen werden.
Ein Objekt vom Typ windows_handle wird mit einem Handle initialisiert. Windows
verwendet Handles, um Ressourcen eindeutig zu identifizieren. Sie erhalten zum Beispiel
einen Handle, wenn Sie die Funktion OpenProcess() aufrufen. Diese gibt einen Handle vom
Typ HANDLE zurück, über den auf einen momentan laufenden Prozess zugegriffen werden
kann. Im obigen Beispiel ist dies der eigene Prozess - also das Programm selbst.
Über den Handle wird in diesem Beispiel die Priorität erhöht, so dass dem Programm unter
Windows mehr Rechenzeit zur Verfügung gestellt wird. Dies geschieht ausschließlich zur
Illustration und macht für obiges Programm nicht wirklich Sinn. Das entscheidende ist
jedoch, dass die Ressource, die mit OpenProcess() geöffnet wurde, in der Funktion main()
nicht mit CloseHandle() geschlossen werden muss. Selbstverständlich würde die Ressource
automatisch geschlossen werden, wenn das Programm beendet wird. In größeren und
komplexeren Programmen würde die Klasse windows_handle aber sicherstellen, dass eine
Ressource dann geschlossen wird, wenn sie nicht mehr benötigt wird. In dem Moment, in
dem der Gültigkeitsbereich des entsprechenden Objekts endet - im obigen Beispiel von h am
Ende der Funktion main() - wird der Destruktor aufgerufen, der automatisch die Ressource
schließt.
2.3 Scoped Pointer
Ein scoped pointer ist ein Zeiger, der alleiniger Eigentümer eines dynamisch reservierten
Objekts ist. Die entsprechende Klasse heißt boost::scoped_ptr und ist in der Headerdatei
boost/scoped_ptr.hpp definiert. Im Unterschied zu std::auto_ptr kann ein scoped
pointer das Eigentum nicht an einen anderen scoped pointer übertragen. Einmal mit einer
3. Adresse initialisiert wird das dynamisch reservierte Objekt dann freigegeben, wenn der
Destruktor ausgeführt wird.
Da ein scoped pointer einfach nur eine Adresse speichert und alleiniger Eigentümer eines
Objekts ist, ist die Implementation von boost::scoped_ptr einfacher als von
std::auto_ptr. Da das Eigentum nicht wie bei std::auto_ptr auf andere Objekte
übertragen werden kann, kann boost::scoped_ptr die bessere Wahl sein, weil das
Eigentum auch nicht versehentlich übertragen werden kann.
#include <boost/scoped_ptr.hpp>
int main()
{
boost::scoped_ptr<int> i(new int);
*i = 1;
*i.get() = 2;
i.reset(new int);
}
Der smart pointer boost::scoped_ptr kann lediglich initialisiert werden, um dann über
überladene Operatoren auf das referenzierte Objekt so zuzugreifen, wie Sie es von
herkömmlichen Zeigern gewöhnt sind. Im Detail sind operator*(), operator->() und
operator bool() überladen. Daneben stehen die beiden Methoden get() und reset() zur
Verfügung: Während get() die Adresse des Objekts zurückgibt, auf das der smart pointer
verweist, kann mit reset() eine neue Adresse im smart pointer gespeichert werden. Ein bis
dahin referenziertes Objekt wird automatisch zerstört.
Im Destruktor von boost::scoped_ptr wird das referenzierte Objekt mit delete
freigegeben. Daher dürfen Sie boost::scoped_ptr nicht mit der Adresse eines dynamisch
reservierten Arrays initialisieren, da dieses mit delete[] freigegeben werden müsste. Sie
müssen in diesem Fall die Klasse boost::scoped_array verwenden, die im Folgenden
vorgestellt wird.
2.4 Scoped Array
Ein scoped array wird wie ein scoped pointer verwendet. Der entscheidende Unterschied ist,
dass der Destruktor eines scoped arrays dynamisch reservierten Speicher mit delete[]
freigibt. Da ausschließlich Arrays mit delete[] freigegeben werden, müssen Sie ein scoped
array mit der Adresse eines dynamisch reservierten Arrays initialisieren.
Die entsprechende Klasse für scoped arrays ist boost::scoped_array und befindet sich in
der Headerdatei boost/scoped_array.hpp.
#include <boost/scoped_array.hpp>
int main()
{
boost::scoped_array<int> i(new int[2]);
*i.get() = 1;
i[1] = 2;
i.reset(new int[3]);
}
4. Die Klasse boost::scoped_array überlädt die beiden Operatoren operator[]() und
operator bool(). Über operator[]() können Sie direkt auf eine Position im Array
zugreifen - ein Objekt vom Typ boost::scoped_array verhält sich also wie das Array,
dessen Adresse es speichert.
So wie bei boost::scoped_ptr stehen ebenfalls die beiden Methoden get() und reset()
zur Verfügung, mit denen die im boost::scoped_array gespeicherte Adresse abgerufen
und neugesetzt werden kann.
2.5 Shared Pointer
Dieser smart pointer wird in der Praxis sehr häufig eingesetzt und wurde in der ersten
Version des C++ Standards schmerzlich vermisst. Er ist im sogenannten Technical Report 1 in
den C++ Standard aufgenommen worden. Wenn Ihre Entwicklungsumgebung den Technical
Report 1 unterstützt, können Sie die Klasse std::shared_ptr aus der Headerdatei memory
nutzen. In der Boost C++ Bibliothek heißt dieser smart pointer boost::shared_ptr und
befindet sich in der Headerdatei boost/shared_ptr.hpp.
Der smart pointer boost::shared_ptr ist grundsätzlich boost::scoped_ptr sehr ähnlich.
Der entscheidende Unterschied ist, dass boost::shared_ptr nicht exklusiver Eigentümer
eines Objekts ist, sondern mit anderen smart pointern vom Typ boost::shared_ptr das
Eigentum teilen kann: Das Objekt, dessen Adresse in mehreren shared pointern gespeichert
ist, wird erst dann zerstört, wenn der letzte shared pointer zerstört ist.
Da boost::shared_ptr das Eigentum teilen kann, können Kopien dieses smart pointers
erstellt werden - etwas, was mit boost::scoped_ptr nicht funktioniert. Dies erst
ermöglicht, smart pointer in Containern zu speichen - etwas, was auch nicht mit
std::auto_ptr möglich ist, da bei jeder Kopie dieses smart pointers der Eigentümer
wechselt.
#include <boost/shared_ptr.hpp>
#include <vector>
int main()
{
std::vector<boost::shared_ptr<int> > v;
v.push_back(boost::shared_ptr<int>(new int(1)));
v.push_back(boost::shared_ptr<int>(new int(2)));
}
Dank des smart pointers boost::shared_ptr ist es wie oben gezeigt möglich, Adressen von
dynamisch reservierten Objekten sicher in Containern zu speichern. Der Container speichert
zwar Kopien und muss eventuell zusätzliche Kopien erstellen, wenn bereits gespeicherte
Elemente neu arrangiert werden müssen. Da boost::shared_ptr jedoch das Eigentum an
Objekten teilen kann, sind Kopien gleichwertig. Dies ist wie bereits beschrieben bei
std::auto_ptr nicht der Fall, weswegen ein std::auto_ptr auch niemals in einem
Container gespeichert werden sollte.
Die Klasse boost::shared_ptr überlädt ähnlich wie boost::scoped_ptr die Operatoren
operator*(), operator->() und operator bool(). Darüberhinaus stehen ebenfalls
5. wieder die Methoden get() und reset() zur Verfügung, um die gespeicherte Adresse
abzurufen und eine neue Adresse zu setzen.
#include <boost/shared_ptr.hpp>
int main()
{
boost::shared_ptr<int> i1(new int(1));
boost::shared_ptr<int> i2(i1);
i1.reset(new int(2));
}
Im obigen Beispiel werden zwei shared pointer i1 und i2 verwendet, die anfangs auf das
gleiche Objekt vom Typ int zeigen. Während die Variable i1 direkt mit einer Adresse
initialiert wird, erhält die Variable i2 über den Copy-Konstruktor die in i1 gespeicherte
Adresse. Wenn dann für i1 die Methode reset() aufgerufen wird, wird die in i1
gespeicherte Adresse neu gesetzt. Das bis dato in i1 gespeicherte Objekt wird jedoch nicht
zerstört, da es noch in der Variablen i2 gespeichert ist. Der smart pointer
boost::shared_ptr zählt also mit, wie viele shared pointer momentan auf ein Objekt
verweisen, und zerstört dieses erst dann, wenn der Gültigkeitsbereich des letzten shared
pointers endet.
Der smart pointer boost::shared_ptr besitzt eine Besonderheit, die es erlaubt anzugeben,
wie ein Objekt zerstört werden soll. Dies erfolgt standardmäßig mit delete, kann jedoch wie
im folgenden Beispiel zu sehen auch anders geschehen.
#include <boost/shared_ptr.hpp>
#include <windows.h>
int main()
{
boost::shared_ptr<void> h(OpenProcess(PROCESS_SET_INFORMATION, FALSE,
GetCurrentProcessId()), CloseHandle);
SetPriorityClass(h.get(), HIGH_PRIORITY_CLASS);
}
Dem Konstruktor von boost::shared_ptr kann als zweiter Parameter eine Funktion oder
ein Funktionsobjekt übergeben werden. Im obigen Beispiel ist dies die Windows API-
Funktion CloseHandle(). Endet der Gültigkeitsbereich der Variablen h, wird nicht delete
aufgerufen, sondern die als zweiter Parameter angegebene Funktion. Diese muss mit einem
einzigen Parameter vom Typ HANDLE aufgerufen werden können, damit der Code kompiliert.
Dies ist bei CloseHandle() der Fall.
Dieses Beispiel funktioniert genauso wie das zu Beginn dieses Kapitels vorgestellte
Programm, das RAII veranschaulicht hat. Anstatt eine eigene Klasse windows_handle zu
definieren, wird hier die Besonderheit von boost::shared_ptr ausgenutzt und dem
Konstruktor als zweiter Parameter eine Funktion übergeben, die automatisch aufgerufen
wird, wenn der Gültigkeitsbereich des shared pointers endet.
6. 2.6 Shared Array
Ein shared array funktioniert grundsätzlich wie ein shared pointer. Der entscheidende
Unterschied ist, dass der Destruktor eines shared arrays delete[] aufruft. Das wiederum
bedeutet, dass ein shared array mit der Adresse eines dynamisch reservierten Arrays
initialisiert werden muss - denn nur diese dürfen mit delete[] freigegeben werden. In den
Boost C++ Bibliotheken finden Sie die Klasse boost::shared_array in der Headerdatei
boost/shared_array.hpp.
#include <boost/shared_array.hpp>
#include <iostream>
int main()
{
boost::shared_array<int> i1(new int[2]);
boost::shared_array<int> i2(i1);
i1[0] = 1;
std::cout << i2[0] << std::endl;
}
Ähnlich wie beim shared pointer kann auch bei einem shared array das Eigentum mit
anderen shared arrays geteilt werden. So teilen sich im obigen Programm die beiden
Variablen i1 und i2 das Eigentum an einem dynamisch reservierten Array. Wenn über
operator[]() auf i1 zugegriffen wird, um den Wert 1 im Array zu speichern, kann über i2
auf das gleiche dynamisch reservierte Array zugegriffen werden und der Wert zum Beispiel
auf die Standardausgabe ausgegeben werden.
So wie alle bisher in diesem Kapitel vorgestellten smart pointer bietet auch
boost::shared_array die Methoden get() und reset() an. Außerdem ist ebenfalls der
Operator operator bool() überladen.
2.7 Weak Pointer
Alle bisher in diesem Kapitel kennengelernten smart pointer können jeweils für sich allein
genommen in unterschiedlichen Situationen verwendet werden. Der weak pointer jedoch
macht nur im Zusammenspiel mit dem shared pointer Sinn. Er ist in der Headerdatei
boost/weak_ptr.hpp als boost::weak_ptr definiert.
#include <windows.h>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <iostream>
DWORD WINAPI reset(LPVOID p)
{
boost::shared_ptr<int> *sh = static_cast<boost::shared_ptr<int>*>(p);
sh->reset();
return 0;
}
DWORD WINAPI print(LPVOID p)
{
boost::weak_ptr<int> *w = static_cast<boost::weak_ptr<int>*>(p);
boost::shared_ptr<int> sh = w->lock();
7. if (sh)
std::cout << *sh << std::endl;
return 0;
}
int main()
{
boost::shared_ptr<int> sh(new int(99));
boost::weak_ptr<int> w(sh);
HANDLE threads[2];
threads[0] = CreateThread(0, 0, reset, &sh, 0, 0);
threads[1] = CreateThread(0, 0, print, &w, 0, 0);
WaitForMultipleObjects(2, threads, TRUE, INFINITE);
}
Die Klasse boost::weak_ptr muss immer mit einem boost::shared_ptr initialisiert
werden. Einmal initialisiert bietet sie genaugenommen nur eine einzige nützliche Methode
an, nämlich lock(). Diese Methode gibt einen boost::shared_ptr zurück, der das
Eigentum mit dem shared pointer teilt, mit dem der weak pointer initialisiert wurde. Für den
Fall, dass der shared pointer, der bei der Initialisierung angegeben wurde, nicht mehr auf ein
Objekt zeigt, ist auch der von lock() zurückgegebene shared pointer auf 0 gesetzt.
Der weak pointer bietet sich an, wenn eine Funktion mit einem in einem shared pointer
verwalteten Objekt arbeiten soll, die Lebensdauer dieses Objekts jedoch nicht von dieser
Funktion abhängen soll. Die Funktion soll also nur dann mit dem Objekt arbeiten, wenn es
momentan in shared pointern, die an anderen Stellen im Programm existieren, verankert ist.
Werden diese shared pointer resettet, soll das entsprechende Objekt nicht mehr existieren
und nicht unnötigerweise durch einen zusätzlichen shared pointer in der entsprechenden
Funktion am Leben gehalten werden.
Im obigen Beispiel werden in main() zwei Threads erstellt. Dazu wird auf Funktionen
zugegriffen, die vom Betriebssystem Windows zur Verfügung gestellt werden. Obiges
Beispiel lässt sich daher ausschließlich unter Windows kompilieren und ausführen.
Der eine Thread führt die Funktion reset() aus, der ein Zeiger auf einen shared pointer
übergeben wird. Der andere Thread besteht aus der Funktion print(), die einen Zeiger auf
einen weak pointer erhält. Dieser weak pointer ist in der Funktion main() mit dem shared
pointer initialisiert worden.
Wenn das Programm gestartet wird, werden die beiden Funktionen reset() und print()
gleichzeitig in zwei Threads ausgeführt. Es lässt sich dabei nicht vorhersagen, in welcher
Reihenfolge die verschiedenen Anweisungen in den Threads abgearbeitet werden. Das
Problem dabei 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 also sein, dass in der Funktion reset() das Objekt zerstört wird,
während gerade in der Funktion print() genau auf dieses Objekt zugegriffen wird, um es
auszugeben.
Der weak pointer löst diese Problematik wie folgt: Beim Aufruf von lock() wird ein shared
pointer zurückgeben. Dieser shared pointer zeigt auf ein Objekt, wenn dieses Objekt zum
Zeitpunkt des Aufrufes existiert. Andernfalls ist der shared pointer auf 0 gesetzt und somit
ein typischer Null-Zeiger.
8. Der weak pointer allein hat demnach keinen Einfluss auf die Lebensdauer eines Objekts.
Damit trotzdem in der Funktion print() gefahrlos auf das Objekt zugegriffen kann, ohne
dass es plötzlich in einem anderen Thread zerstört wird, gibt lock() einen shared pointer
zurück. Selbst wenn nun in einem anderen Thread versucht wird, das Objekt zu zerstören,
existiert es dank des von lock() zurückgegebenen shared pointers weiter.
2.8 Intrusive Pointer
Der intrusive pointer funktioniert genauso wie der shared pointer. Während
boost::shared_ptr jedoch automatisch mitzählt, wie viele shared pointer momentan auf
ein Objekt zeigen und das Eigentum teilen, muss beim intrusive pointer der Entwickler
mitzählen. Das ist zum Beispiel dann von Vorteil, wenn auf Klassen aus Frameworks
zugegriffen wird, die ihrerseits sowieso schon mitzählen.
In den Boost C++ Bibliotheken befindet sich der intrusive pointer in der Headerdatei
boost/intrusive_ptr.hpp und heißt boost::intrusive_ptr.
#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));
CComDispatchDriver dd(disp.get());
CComVariant arg("C:Windows");
CComVariant ret(false);
dd.Invoke1(CComBSTR("FolderExists"), &arg, &ret);
std::cout << (ret.boolVal != 0) << std::endl;
}
void main()
{
CoInitialize(0);
check_windows_folder();
CoUninitialize();
}
Im obigen Beispiel wird auf COM-Funktionen zugegriffen, so dass der Code ausschließlich
unter Windows kompiliert und ausgeführt werden kann. COM-Objekte bieten sich insofern
als Beispiel für boost::intrusive_ptr an als dass sie mitzählen, wie viele Zeiger momentan
9. auf sie verweisen. Der interne Zä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 nun innerhalb der Funktionen
intrusive_ptr_add_ref() und intrusive_ptr_release() aufgerufen, um den internen
Zähler im COM-Objekt zu inkrementieren und dekrementieren. Das COM-Objekt, das in
diesem Beispiel 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 den
Ordner C:Windows gibt. Wie das 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 des
intrusive pointers 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.
2.9 Pointer Container
Nachdem Sie nun die verschiedenen smart pointer der Boost C++ Bibliotheken
kennengelernt haben, können Sie für dynamisch reservierte Objekte und Arrays sicheren
Code schreiben. Häufig müssen Objekte in Containern verwaltet werden, was wie Sie bereits
gesehen haben mit boost::shared_ptr und boost::shared_array auch problemlos
funktioniert.
#include <boost/shared_ptr.hpp>
#include <vector>
int main()
{
std::vector<boost::shared_ptr<int> > v;
v.push_back(boost::shared_ptr<int>(new int(1)));
v.push_back(boost::shared_ptr<int>(new int(2)));
}
Während der Code im obigen Beispiel völlig korrekt ist und smart pointers so angewandt
werden könnten, ist das wegen der wiederholten Angabe von boost::shared_ptr nicht nur
mehr Schreibarbeit. Das Kopieren von boost::shared_ptr in, aus und innerhalb von
Containern erfordert ein ständiges In- und Dekrementieren von Zählern und ist daher
weniger effizient. Die Boost C++ Bibliotheken bieten daher Pointer Container an, die auf die
Verwaltung dynamisch reservierter Objekte spezialisiert sind.
#include <boost/ptr_container/ptr_vector.hpp>
int main()
{
boost::ptr_vector<int> v;
v.push_back(new int(1));
v.push_back(new int(2));
}
10. Die Klasse boost::ptr_vector, die in der Headerdatei
boost/ptr_container/ptr_vector.hpp definiert ist, funktioniert genauso wie der im
vorherigen Beispiel verwendete Container, der mit dem Template-Parameter
boost::shared_ptr instantiiert ist. Dadurch, dass boost::ptr_vector auf dynamisch
reservierte Objekte spezialisiert ist, ist diese Klasse effizienter und einfacher anzuwenden.
Da boost::ptr_vector jedoch alleiniger Eigentümer aller Objekte ist, kann nicht wie bei
std::vector<boost::shared_ptr<int> > das Eigentum zum Beispiel mit einem shared
pointer geteilt werden, der nicht im Container gespeichert ist.
Neben boost::ptr_vector stehen weitere Container zur Verfügung, die auf die Verwaltung
dynamisch reservierter Objekte spezialisiert sind. Dazu gehören boost::ptr_deque,
boost::ptr_list, boost::ptr_set, boost::ptr_map, boost::ptr_unordered_set und
boost::ptr_unordered_map. Diese Container entsprechen den aus dem C++ Standard
bekannten Containern. Die letzten beiden genannten Container entsprechen den Containern
std::unordered_set und std::unordered_map, die im Technical Report 1 dem C++
Standard hinzugefügt wurden. Sie sind außerdem als boost::unordered_set und
boost::unordered_map in der Boost C++ Bibliothek Unordered definiert, die Sie einsetzen
können, wenn die von Ihnen verwendete Implementation des C++ Standards den Technical
Report 1 nicht unterstützt.
2.10 Aufgaben
Verbessern Sie folgendes Programm, indem Sie einen geeigneten smart pointer verwenden:
1. #include <iostream>
2. #include <cstring>
3.
4. char *get(const char *s)
5. {
6. int size = std::strlen(s);
7. char *text = new char[size + 1];
8. std::strncpy(text, s, size + 1);
9. return text;
10. }
11.
12. void print(char *text)
13. {
14. std::cout << text << std::endl;
15. }
16.
17. int main(int argc, char *argv[])
18. {
19. if (argc < 2)
20. {
21. std::cerr << argv[0] << " <data>" << std::endl;
22. return 1;
23. }
24.
25. char *text = get(argv[1]);
26. print(text);
27. delete[] text;
28. }
Verbessern Sie folgendes Programm: