Schulung C++ Boost Bibliotheken

1,147 views

Published on

Published in: Technology, Education
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,147
On SlideShare
0
From Embeds
0
Number of Embeds
23
Actions
Shares
0
Downloads
0
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Schulung C++ Boost Bibliotheken

  1. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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:
  11. 11. 29. #include <vector> 30. 31. template <typename T> 32. T *create() 33. { 34. return new T; 35. } 36. 37. int main() 38. { 39. std::vector<int*> v; 40. v.push_back(create<int>()); 41. }

×