Orffi Geschrieben 10. April 2002 Geschrieben 10. April 2002 Da ISO-C++ keinen Garbage Collector mitbringt, muß der Programmierer dafür sorgen, daß dynamisch reservierter Speicher wieder freigegeben wird. Man wird also zu jedem new ein delete im Quellcode finden. Leider sieht die Welt nicht immer ganz so einfach aus. Ein kleines Beispiel: Man hat eine Funktion, die Speicher dynamisch anfordert und am Ende wieder freigibt. Allerdings wird mitten in der Funktion, also zwischen new und delete, eine Exception ausgelöst. void f (void) { X *obj = new X(); //mache irgendetwas... throw std::exception (); delete obj; } (Am Ende steht der komplette Quelltext, der auch kompilierbar ist.) Bei einem solchen Problem kommt der auto_ptr aus <memory> zum Einsatz. Denn, wie jeder leicht sieht, wird der Speicher nicht freigegeben und man hat ein Speicherloch, denn man hat keine Möglichkeit mehr auf den Speicher zuzugreifen, um ihn wieder freizugeben. Folgender Code hat so ein Speicherloch nicht: void g ( void ) { std::auto_ptr<X> obj ( new X ( "auto" ) ); throw std::exception (); } auto_ptr<> ruft den Destruktor des Objekt, welches er hält, automatisch auf, wenn die auto_ptr Variable den Sichtbarkeitsbereich verläßt. Die Funktion also normal verlassen wird oder durch eine Exception. Da ein auto_ptr nicht besonders schlau ist, kann er keine Referenzen zählen oder sonstwie herausfinden, wer nun auf das Objekt noch zeigt oder nicht. Deswegen passiert etwas sehr interessantes, wenn man einen auto_ptr in einen anderen kopiert. Nach: std::auto_ptr<X> obj2 = obj1; ist obj1 (was auch ein auto_ptr ist) NULL. Kopien von auto_ptr sind also alles andere als gleich!!! Niemals, wirklich niemals sollte man auf die folgende Idee kommen: std::vector<std::auto_ptr<X>> v; sort ( v.begin(), v.end() ); Hier wird intern natürlich hin- und herkopiert, um den Vector zu sortieren. Und bei jeder Kopieraktion ist das Original ein NULL-Pointer! Dies gilt übrigens nicht nur für vector sondern für alle Standard Container. Wie in Funktion i() (siehe unten) zu sehen ist, wird der auto_ptr wie ein ganz normaler Zeiger benutzt. Mit release() gibt der auto_ptr die Verwaltung für das Objekt auf. Man muß sich dann wieder selbst um die freigabe kümmern. Mit reset ('Objekt') kann man dem auto_ptr ein neues Objekt in die Obhut geben. Dabei wird das alte Objekt freigegeben. Hier nun ein komplettes Programm, das vielmehr erklärt als der ganze Text: #include <iostream> #include <memory> #include <string> #include <exception> class X { std::string name; public: X ( std::string ); ~X () { std::cout << "Destruktor Y: " << name << '\n'; } std::string getName () { return name; } }; X::X ( std::string n) : name ( n ) { std::cout << "Konstruktor Y: " << name << '\n'; } void f ( void ) { X *obj = new X ( "normal" ); throw std::exception (); delete obj; } void g ( void ) { std::auto_ptr<X> obj ( new X ( "auto" ) ); throw std::exception (); } void h ( void ) { std::auto_ptr<X> obj1 ( new X ( "obj1" ) ); { std::auto_ptr<X> obj2 = obj1; } std:: cout << "^ Fuer obj1 wurde der Destruktor schon aufgerufen!?!\n"; // weil das Original nach der Kopieraktion auf NULL zeigt. } void i ( void ) { std::auto_ptr<X> obj1 ( new X ( "Horst" ) ); std:: cout << "Name des Objekts: " << obj1 -> getName () << '\n'; obj1.reset ( new X ( "Karl" ) ); X *obj2 = obj1.release(); } int k ( void ) { std::auto_ptr<X> obj1 ( new X ( "Hugo" ) ); X *obj2 = obj1.release(); delete obj2; } int main ( void ) { try { f(); } catch ( std::exception &e ) { std::cout << e.what () << '\n'; } std::cout << "normal wird nicht freigegeben...\n"; try { g(); } catch ( std::exception &e ) { std::cout << e.what () << '\n'; } h (); i (); std::cout << "Karl wird nicht freigegeben...\n"; k (); return 0; } HTH Jan Zitieren
Crush Geschrieben 10. April 2002 Geschrieben 10. April 2002 Warum sollte man nicht auch in einen solchen Auto-Pointer nicht irgendwie einen Referenzzähler mit einbauen können? Das sollte doch machbar sein. Zitieren
Empfohlene Beiträge
Dein Kommentar
Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.