Advanced Services | Uniquepointer |
unique_ptr sind von der boost Library in die STL gewandert und man kann sie als Nachfolger zu Autopointern betrachten, die mit C++11 deprecated sind. unique_ptr sind Singletons, die nicht kopiert werden können und nicht zugewiesen werden können, denn sowohl der Kopierkonstruktor als auch der Zuweisungsoperator sind private. Dagegen sind der move-Konstruktor und move-Assignment präsent. "transfer of ownership" ist vermittels std::move() möglich und führt zur Zerstörung der Quelle. Man kann eigene Deleter in Form von Funktionen oder Klassen schreiben, ebenso eigene Klassen, die das komplette Memorymanagement übernehmen können. Eine Reihe von Beispielen wird dieses Verhalten demonstrieren.
class Test { public: int i; Test(int j = 0) : i(j) {} ~Test() { cout << "destructor test\n" << endl; } };
void testdeleterfree(Test* t) { std::cout << "testdeleterfree called" << std::endl; free(t); };
void testdeleternew(Test* t) { std::cout << "testdeleternew called" << std::endl; delete t; };
// void operator() ist notwendig, in welcher Form auch immer struct CustomDeleterClass { void operator()(Test* pt) { cout << "custom deleter operator() in CustomDeleterClass" << endl; delete pt; } };
struct CustomMemoryManager { Test* alloc() { cout << "custom allocator in CustomMemoryManager" << endl; return new Test(); } void operator()(Test* pt) { cout << "custom deleter operator() in CustomMemoryManager" << endl; delete pt; } };
void foo(std::unique_ptr<Test> up) { cout << "foo(std::unique_ptr<Test>) called: up->i = " << up->i <<endl; }
void uniqueptr1() { cout << "basics\n" << endl; cout << "Zwei unique_ptr anlegen" << endl; std::unique_ptr<Test> up1( new Test(17) ); std::unique_ptr<Test> up2( new Test(42) ); //std::unique_ptr<Test> up3(up2); // Compilerfehler: Kopierkonstruktor ist private //up1 = up2; // Compilerfehler: operator= ist private überschrieben // * und -> sind überladen cout << (*up1).i << endl; // 17 cout << up2->i << endl; // 42 cout << up1.get() << endl; // Adresse des Raw-pointers // << überladen und gibt immer 1 aus (joke) cout << up1 << endl; // 1 }
Die Ausgabe
Es ist offensichtlich wie swap arbeitet.
void uniqueptr2a() { cout << "begin swap\n" << endl; std::unique_ptr<Test> up1( new Test(17) ); std::unique_ptr<Test> up2( new Test(42) ); cout << "up1->i = " << up1->i << endl; // 17 cout << "up2->i = " << up2->i << endl; // 42 up1.swap(up2); cout << "nach swap" << endl; cout << "up1->i = " << up1->i << endl; // 42 cout << "up2->i = " << up2->i << endl; // 17 cout << "\nend swap" << endl; }
Die Ausgabe
Hier wird die Funktionm foo() verwendet.
void uniqueptr2b() { cout << "begin transfer of ownership\n" << endl; std::unique_ptr<Test> up( new Test(17) ); cout << "unique_ptr initialisiert" << endl; cout << "up->i = " << up->i << endl; // 17 cout << "mit Hilfe von move an Funktion uebergeben" << endl; foo( std::move(up)) ; // einzige Möglichkeit der Übergabe cout << "nach der Uebergabe" << endl; // NULL cout << "unique_ptr ist " << ((up == NULL) ? "NULL" : " nicht NULL" ) << endl; cout << "\nend transfer of ownership" << endl; }
Die Ausgabe
void uniqueptr3() { cout << "custom-deleters\n" << endl; // Bekanntgabe eines custom-deleters, hier std::free falls speicher mit malloc angeschafft wird std::unique_ptr<Test, void (*)(void*)> up1( (Test*)malloc(sizeof(Test)) , std::free ); // custom-deleter der free() verwendet std::unique_ptr<Test, void (*)(Test*)> up2( (Test*)malloc(sizeof(Test)) , testdeleterfree ); // custom deleter der delete verwendet, delete ruft den Destruktor! std::unique_ptr<Test, void (*)(Test*)> up3( new Test() , testdeleternew ); }
Die Ausgabe
void uniqueptr4() { cout << "custom-deleters 2\n" << endl; // variante 1 std::unique_ptr<Test, CustomDeleterClass> up1( new Test() ); // variante 2 CustomMemoryManager cmm; std::unique_ptr<Test, CustomMemoryManager> up2( cmm.alloc() ); }