Kopierkonstruktor passt das so?



  • 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