Advanced  Services Uniquepointer Back Next Up Home


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.


Die Testklasse
class Test
{
public:
   int i;
   Test(int j = 0) : i(j) {}
   ~Test() { cout << "destructor test\n" << endl; }
};

Die Deleterfunktionen
void testdeleterfree(Test* t)
{
   std::cout << "testdeleterfree called" << std::endl;
   free(t);
};
void testdeleternew(Test* t)
{
   std::cout << "testdeleternew called" << std::endl;
   delete t;
};

Die Klassen für das Memorymanagement
// 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;
   }
};

Eine Funktion mit einem unique_ptr als Parameter
void foo(std::unique_ptr<Test> up)
{
   cout << "foo(std::unique_ptr<Test>) called: up->i = " << up->i <<endl;
}

Das erste Beispiel zeigt elementare Eigenschaften

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

unique-pointer-1.jpg


swap

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

unique-pointer-2.jpg


transfer of ownership mit move

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

unique-pointer-2b.jpg


Verwendung der Deleterfunktionen

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

unique-pointer-3.jpg


Verwendung der Deleterklasse und der MemoryManagementklasse

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() );

}

unique-pointer-4.jpg

Valid XHTML 1.0 Strict top Back Next Up Home