Kopierkonstruktor passt das so?



  • Braunstein schrieb:

    Entschuldigung, aber deine Version des CopyCTors ist ziemlicher Unsinn. Wenn da eine Exception auftritt machst du zwar ein delete weist aber danach die ungültigen Zeiger zu. Was willst du mit dem ungültigen Objekt dann überhaupt machen?
    Wenn in einem Konstruktor oder CopyKonstruktor eine Exception fliegt sollte die nach draußen gehen, und dem Programm sagen, dass dein Objekt nicht erstellte werden konnte.

    Ups, hatte das erneute Werfen der Ausnahme vergessen :o

    Und was hast du da bei einem Fehler? Eine benannte Katze ohne Alter und Gewicht. Wenn bei der Initialisierung etwas schiefgegangen ist, sollte auf keinen Fall ein halbfertiges Objekt zurückbleiben (das ist mitunter noch schlimmer als ein Speicherleck).

    Das mit der Konsistenz war hier vielleicht fehl am Platz. Das ist bei ähnlicher Nutzung eher auf den Zuweisungsoperator anzuwenden.
    Aber hier auch das gleiche: ich hatte das throw vergessen. Wenn nun keiner die Exception abfängt, kann ich das auch nicht ändern. Wäre aber bei der Version oben auch nicht anders: entweder man hat ein Speicherleck oder zwei 0-Zeiger. Wenn man diese nicht weiter überprüft geht man damit auch baden. So kann man aber die Ausnahmen abfangen und das Programm suber beenden.



  • Wenn die Exception vom Programm nicht gefangen wird, beendet sich das Programm sowieso und dann spielen Speicherlecks auch keine Rolle mehr.



  • Mh, sagen wir mal, ich lege da zwei Objekte an die, die jeweils 1 MB groß sind. Außerdem unterstützt mein Betriebssystem kein Garbage Collection.
    Nun kriege ich für das 2. Objekt keinen Speicher und das erste liegt als Leiche im Speicher. 1MB verschwendet ... Weit hergeholt, weiß ich selber, ich bin lieber auf alle Eventualitäten vorbereitet, als das am Ende doch mal was schief geht.
    Bezogen auf den op=: wenn nun jemand die Ausnahme abfängt und dafür sorgt, dass das Programm ansonsten normal weiterläuft, habe ich als Ersteller dieser Klasse dafür Sorge zu tragen, dass ein Objekt nach so einem Fehlschlag konsistent bleibt. Bei dem Kopierkonstruktor sieht das natürlich anders aus, weil es ja vorher gar kein Objekt gab.



  • Nun, die Betriebssysteme, die ich kenne räumen den Speicher nach beendigung eines programmes schon auf.
    Beim op= ist das natürlich anders. Hier kann man aber schön das swap-Idiom von Herb sutter verwenden und hat dann auch keine Probleme mehr.



  • struct Foo {
         Foo() : a(0), b(0) {
              Megabyte* a = new Megabyte;
              Megabyte* b = new Megabyte;
         }
         ~Foo() {
              delete a; delete b;
         }
         Megabyte* a,b;
    };
    

    Das geht doch völlig in Ordnung. Wenn ein new schiefgeht können 2 Dinge passieren:
    - Die Exception wird nicht gefangen, dann ist eh alles egal - Ende Gelände.
    - Die Exception wird gefangen, dann werden auch die Objekte vom Stack genommen, der Destruktor übernimmt das Löschen der bereits angelegten Objekte.



  • und wenn du beim op= von Megabyte ne exception hast biste verratzt



  • Ich sehe da kein Problem...

    //in Foo
    Foo& operator=(Foo const& rhs) {
        delete a; delete b;
        a = b = 0;
        a = new Megabyte(*rhs.a);
        b = new Megabyte(*rhs.b);
    }
    

    hier speziell braucht man noch ein this != &rhs



  • Oh du hast von Megabyte geschrieben das hab ich gar net gesehn. Was hat der mit der ganzen Sache zu tun, den verwend ich nie...



  • Genau hier gibts ein Problem. Wenn das erste new im op= eine Exception wirft hast du ein uninitialisiertes Objekt. Mach es so

    struct Foo {
         Foo() : a(new Megabyte), b(new Megabyte) {}
         ~Foo() {
              delete a; delete b;
         }
         void Swap(Foo& src)
         {
            std::swap(a, src.a);
            std::swap(b, src.b);
         }
        Foo& operator=(Foo const& rhs) 
        {
           Foo tmp(rhs);
           Swap(tmp);
           return *this;
        }
         Megabyte* a,b;
    };
    


  • Oder nimm einfach shared_ptr aus boost oder tr1


  • Mod

    gesunder Menschenverstand schrieb:

    - Die Exception wird gefangen, dann werden auch die Objekte vom Stack genommen, der Destruktor übernimmt das Löschen der bereits angelegten Objekte.

    Keineswegs. Der Destruktor wird nur für lebende Objekte aufgerufen, das heißt, der Konstruktor muss normal beendet worden sein. Das Werfen einer Exception innerhalb des Konstruktors (und sei es in der Initialisierungsliste) verhindert das. Grundsätzlich ist das Problem ohne RAII für mehr als eine Resource, deren Anforderung scheitern kann, kaum zu bändigen. Möglich ist es, aber nicht empfelenswert:

    struct Foo
    {
        Foo()
        try
        : a( ( a = NULL, new Megabyte() ) ),
          b( new Megabyte() )
        {}
        catch (...)
        {
            delete a;
        }
        Foo(const Foo& other)
        try
        : a( ( a = NULL, new Megabyte( *other.a ) ) ),
          b( new Megabyte( *other.b ) )
        {}
        catch (...)
        {
            delete a;
        }
         ~Foo()
         {
              delete b;
              delete a;
         }
        Foo& operator=(Foo const& rhs)
        {
            Megabyte* new_a = new Megabyte( *rhs.a );
            try
            {
                Megabyte* new_b = new Megabyte( *rhs.b );
                delete b;
                delete a;
                a = new_a;
                b = new_b;
                return *this;
            }
            catch (...)
            {
                delete new_a;
                throw;
            }
        }
        Megabyte* a;
        Megabyte* b;
    };
    

    Bei Verwendung von scoped_ptr, auto_ptr oder ähnlichem wird das Ganze dagegen fast trivial.



  • Warum löscht du im try Block nur a? 😕
    Soweit ich weis ist es save auch nen null pointer zu löschen, ein delete b würde also nichts schaden..



  • Bei seiner Variante ist b kein Nullpointer. Warum sollte er b auch löschen? Wenn eine Exception fliegt ist b sowieso nicht initialisiert und wenn keine fliegt muß man auch nichts löschen.


Anmelden zum Antworten