Destruktor von Subklasse über Basis?



  • manni_meier schrieb:

    } finally {
    delete base;
    }
    }[/cpp]

    finally gibt es in C++ nicht. Das kann höchstens eine Compiler Extension sein, welche jedoch nicht portabel zu anderen Compiler ist und daher ist es nicht ratsam darauf zurück zugreifen.

    BR



  • Argh, sry, stimmt...
    Hab wohl in letzter Zeit zu viel mit D gespielt.

    @Mati:
    Könntest du mal genau sagen, an welcher Stelle etwas (und auch WAS) deiner Meinung nach nicht läuft?
    Welches Objekt wird ohne Exception abgebaut?



  • Puh - also im Moment weiß ich echt nicht weiter wie ich das Problem angehen soll...

    Der Zeiger wird zerstört, aber das Objekt was du mit new erzeugt hast, bleibt erhalten. Am einfachsten wäre es, wenn du die Objekte nicht mit new erzeugst, sondern auf dem Stack(heißt der so?), also einfach "Mtx mtx;".

    meinst du damit dass ich alle member der klasse die innerhalb d1/d2 liegt auf dem stack anlegen sollte? Allgemeine Frage: Wie lang leben denn die Member eines Objektes (O1) das selbst Member eine Objektes (O2) ist? So lange wie O2 lebt? Also ist das toplevel Objekt immer verantwortlich wie lange alle submember leben?

    Also folgende Ausgabe - ich hoffe die Hilft weiter:

    valgrind --show-reachable=yes --leak-check=full ./out
    ==14218== Memcheck, a memory error detector.
    ==14218== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
    ==14218== Using LibVEX rev 1658, a library for dynamic binary translation.
    ==14218== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
    ==14218== Using valgrind-3.2.1-Debian, a dynamic binary instrumentation framework.
    ==14218== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
    ==14218== For more details, rerun with: -v
    ==14218==
    ==14218==
    ==14218== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 17 from 1)
    ==14218== malloc/free: in use at exit: 120 bytes in 4 blocks.
    ==14218== malloc/free: 6 allocs, 2 frees, 226 bytes allocated.
    ==14218== For counts of detected errors, rerun with: -v
    ==14218== searching for pointers to 4 not-freed blocks.
    ==14218== checked 109,052 bytes.
    ==14218==
    ==14218== 120 (12 direct, 108 indirect) bytes in 1 blocks are definitely lost in loss record 1 of 4
    ==14218==    at 0x4021DC5: operator new(unsigned) (vg_replace_malloc.c:163)
    ==14218==    by 0x8048D52: D1::bar() (in /home/mat/tmp/test/out)
    ==14218==    by 0x8048C35: D1::foo() (in /home/mat/tmp/test/out)
    ==14218==    by 0x8048B6C: main (in /home/mat/tmp/test/out)
    ==14218==
    ==14218==
    ==14218== 28 bytes in 1 blocks are indirectly lost in loss record 2 of 4
    ==14218==    at 0x4021A55: operator new[](unsigned) (vg_replace_malloc.c:195)
    ==14218==    by 0x8048D34: Mtx::Mtx() (in /home/mat/tmp/test/out)
    ==14218==    by 0x8048D60: D1::bar() (in /home/mat/tmp/test/out)
    ==14218==    by 0x8048C35: D1::foo() (in /home/mat/tmp/test/out)
    ==14218==    by 0x8048B6C: main (in /home/mat/tmp/test/out)
    ==14218==
    ==14218==
    ==14218== 40 bytes in 1 blocks are indirectly lost in loss record 3 of 4
    ==14218==    at 0x4021A55: operator new[](unsigned) (vg_replace_malloc.c:195)
    ==14218==    by 0x8048D20: Mtx::Mtx() (in /home/mat/tmp/test/out)
    ==14218==    by 0x8048D60: D1::bar() (in /home/mat/tmp/test/out)
    ==14218==    by 0x8048C35: D1::foo() (in /home/mat/tmp/test/out)
    ==14218==    by 0x8048B6C: main (in /home/mat/tmp/test/out)
    ==14218==
    ==14218==
    ==14218== 40 bytes in 1 blocks are indirectly lost in loss record 4 of 4
    ==14218==    at 0x4021A55: operator new[](unsigned) (vg_replace_malloc.c:195)
    ==14218==    by 0x8048D0D: Mtx::Mtx() (in /home/mat/tmp/test/out)
    ==14218==    by 0x8048D60: D1::bar() (in /home/mat/tmp/test/out)
    ==14218==    by 0x8048C35: D1::foo() (in /home/mat/tmp/test/out)
    ==14218==    by 0x8048B6C: main (in /home/mat/tmp/test/out)
    ==14218==
    ==14218== LEAK SUMMARY:
    ==14218==    definitely lost: 12 bytes in 1 blocks.
    ==14218==    indirectly lost: 108 bytes in 3 blocks.
    ==14218==      possibly lost: 0 bytes in 0 blocks.
    ==14218==    still reachable: 0 bytes in 0 blocks.
    ==14218==         suppressed: 0 bytes in 0 blocks.
    

    Und auch mit folgendem bleibt der fehler erhalten - wenn ich nur eines der
    Objekte erstelle: WARUM wird in Zeile 28 NICHT der Destruktor von Mtx aufgerufen???

    #include <iostream>
    #include <stdexcept>
    class Mtx
    {
            public:
                    Mtx() { a = new int[10];
                            b = new double[5];
                            c = new int[7];}
                    ~Mtx() { if (a) delete [] a;
                    if(b) delete [] b;
                    if(c) delete [] c;}
                    int* a;     
                    double* b;
                    int* c; 
    };
    class Base
    {
            public:
               virtual ~Base() { };
               virtual Mtx* bar() = 0;
               virtual void foo() = 0;
    };
    
    class D1 : public Base
    {
            public:
                    D1() { mtx = NULL; }
                    ~D1() { delete mtx; }
                    void foo() {     
                            mtx = bar();                        
                    }
                    Mtx* bar() {
                       Mtx* mtx2 = new Mtx();    
                       //something happens
                       throw std::runtime_error("error");    
                       return mtx2;
                    }
                    Mtx* mtx;
    };
    class D2 : public Base
    {
            public:
                    D2() { mtx = NULL; }
                    ~D2() { delete mtx; }
    
                    void foo() {     
                      mtx = bar();                        
                    }
    
                    Mtx* bar() {
                         Mtx* mtx2 = new Mtx(); 
                         //something happens
                         throw std::runtime_error("error");      
                         return mtx2;
                    }
                    Mtx* mtx;
    };
    
    int main(int argc, char** argv)
    {
            Base * base = NULL;
    
            if (true) base = new D1();
            else base = new D2();
            try {
               base->foo();
            }
            catch ( std::runtime_error& ex )
            {
                    delete base;
                    //do something
                    return 1;
            }
            delete base;
    
            return 0;
    }
    


  • void foo() {    
    mtx = bar(); // diese zuweisung findet nie statt                  
    }
    
    Mtx* bar() {
    Mtx* mtx2 = new Mtx(); // mtx2 zeit auf dynamisch erzeugte Mtx
    //something happens
    throw std::runtime_error("error");   
    // -> abbruch. Der Zeiger mtx2 ist weg. 
    // -> niemand zeigt mehr auf das neue objekt
    // -> niemand kann es löschen   
    return mtx2;
    }
    Mtx* mtx;
    
    #include <array>
    #include <memory>
    #include <stdexcept>
    
    class Mtx
    {
    public:
    	std::tr1::array<int,10> a;    
    	std::tr1::array<double,5> b;
    	std::tr1::array<int,7> c;
    };
    class Base
    {
    public:
    	typedef std::tr1::shared_ptr<Mtx> Mtx_ptr;
    	virtual ~Base() { };
    	virtual Mtx_ptr bar() = 0;
    	virtual void foo() = 0;
    };
    
    class D1 : public Base
    {
    public:
            void foo() 
    		{    
    			mtx_ = bar();                  
            }
    
            Mtx_ptr bar() 
    		{
    			Mtx_ptr p(new Mtx());    
    			//something happens
    			throw std::runtime_error("error");    
    			return p;
            }
    
    private:
    	Mtx_ptr mtx_;
    };
    


  • danke aber:

    void foo() {    
    mtx = bar(); // diese zuweisung findet nie statt                  
    }
    

    warum findet diesee zuweisung nie statt?

    Und was zur HÖlle ist ein typedef std::tr1::shared_ptr<Mtx> Mtx_ptr; ? 🙂



  • Mati schrieb:

    danke aber:

    void foo() {    
    mtx = bar(); // diese zuweisung findet nie statt                  
    }
    

    warum findet diesee zuweisung nie statt?

    Weil bar() eine Exception wirft. Wenn dies passiert, wird der Programmfluss vereinfacht gesprochen abgebrochen, und in den nächsten passenden catch-Block gesprungen. Dabei werden Stackvariablen noch aufgeräumt... [ERGÄNZUNG: Daher kommt es auch nicht zum return...]

    Mati schrieb:

    Und was zur HÖlle ist ein typedef std::tr1::shared_ptr<Mtx> Mtx_ptr; ? 🙂

    1. Der Namensraum std sollte Bekannt sein (Der Namensraum der C++ Standardbibliothek). std::tr1 ist der Namensraum des Technical Reports 1, einer Ergänzung zum Standard.
    2. shared_ptr gehört zu der Gruppe der Smartpointer. Objekte, die sich beispielsweise selbst über Freigaben kümmern.

    Mtx_ptr steht dank dem typedef als verkürzte Schreibweise für std::tr1::shared_ptr<Mtx>.



  • bar() wirft eine ausnahme, d.h. das das Program direkt bis zum nächst passenden catch handler 'springt', also zum den in der main funktion.

    std::tr1::shared_ptr ist eine Templateklasse, die wie ein normaler Zeiger benutzt werden kann, aber die Speicherverwltung übernimmt.
    typedef ist eine Typdefinition, Mtx_ptr ist damit gleichbedeutend mit std::tr1::shared_ptr<Mtx>.

    int main()
    {
        typedef std::tr1::shared_ptr<Mtx> Mtx_ptr;
        {
        Mtx_ptr p(new Mtx);
    
        } //<-- Destruktor von p wird aufgerufen. Dieser löscht das Objekt automatisch
       {
        Mtx_ptr p1(new Mtx);
        {
         Mtx_ptr p2=p1; // zwei shared_ptr zeigen auf 1Objekt
        }//<-- Destruktor von p2 wird aufgerufen. Nihcts passiert
       }//<-- Destruktor von p1 wird aufgerufen. Onjekt wird gelöscht
    
    }
    


  • Danke für die Erklärung: Gäbe es eine Alternative zu Smart-Pointern? Bzw. sind diese über alle gängigen Compiler möglich? Der code soll möglichst portabel bleiben. Ich weiß nicht immer auf den TR1 zugegriffen werden kann ?!



  • Mati schrieb:

    Danke für die Erklärung: Gäbe es eine Alternative zu Smart-Pointern? Bzw. sind diese über alle gängigen Compiler möglich?

    Grundsätzlich sollten alle aktuellen Compiler den TR1 unterstützen (der ist auch schon eine Weile gültig). Zudem kann man auch noch die Boost-Bibliothek nutzen (den die Smartpointer des TR1 sind im wesentlichen Boost entnommen wurden), die auch mit aktuellen Compilern arbeiten sollte.

    Und zu Alternativen:
    a) Versuchen ob man auf dynamische Speicherallozierung verzichten kann, oder versuchen dies durch Container der Standardbibliothek "zu verbergen".
    b) Dafür sorgen das im Exceptionfall noch ein catch-Block die Ressourcen freigegeben werden und dann die Exception weiterwerfen (wenn man sie nicht sinnvoll behandeln kann).

    TR1 und Boost sind für mich ebenso wichtig wie die C++ Standardbibliothek, und sie enthalten viele sehr sinnvolle Dinge. Wobei man bei Smartpointern etwas aufpassen muss (der shared_ptr ist durchaus nützlich, aber auch der "teuerste" der Smartpointer; Sofern der Anwendungsfall Andere unterstützt sind diese vorzuziehen).



  • ja geht scho los...bei mir haut des mim smart-pointern net hin...

    #include <iostream>
    #include <stdexcept>
    #include <array>
    #include <memory>
    #include <stdexcept> 
    class Mtx
    {
            public:
                    Mtx() { a = new int[10];
                            b = new double[5];
                            c = new int[7];}
                    ~Mtx() { if (a) delete [] a;
                    if(b) delete [] b;
                    if(c) delete [] c;}
                    int* a;     
                    double* b;
                    int* c; 
    };
    class Base
    {
            public:
               typedef std::tr1::shared_ptr<Mtx> Mtx_ptr; 
               virtual ~Base() { };
               virtual Mtx_ptr bar() = 0; 
               virtual void foo() = 0;
    };
    
    class D1 : public Base
    {
            public:
                    D1() { mtx = NULL; }
                    ~D1() { delete mtx; }
                    void foo() {     
                            mtx = bar();                        
                    }
                    Mtx_ptr bar()  {
                            Mtx_ptr p(new Mtx());     
                       //something happens
                       throw std::runtime_error("error");    
                       return p;
                    }
            private:
                    Mtx_ptr mtx_; 
    };
    class D2 : public Base
    {
            public:
                    D2() { mtx = NULL; }
                    ~D2() { delete mtx; }
    
                    void foo() {     
                      mtx = bar();                        
                    }
    
                    Mtx_ptr bar()  {
                            Mtx_ptr p(new Mtx()); 
                         //something happens
                         throw std::runtime_error("error");      
                         return p;
                    }
            private:
                    Mtx_ptr mtx_; 
    };
    
    int main(int argc, char** argv)
    {
            Base * base = NULL;
    
            if (true) base = new D1();
            else base = new D2();
            try {
               base->foo();
            }
            catch ( std::runtime_error& ex )
            {
                    delete base;
                    //do something
                    return 1;
            }
            delete base;
    
            return 0;
    }
    
    main.cpp:4:17: error: array: No such file or directory
    main.cpp:23: Fehler: »std::tr1« wurde nicht deklariert
    main.cpp:23: Fehler: ISO-C++ verbietet Deklaration von »shared_ptr« ohne Typ
    main.cpp:23: Fehler: typedef-Name darf kein Spezifizierer mit geschachteltem Namen sein
    main.cpp:23: Fehler: expected »;« before »<« token
    main.cpp:25: Fehler: »Mtx_ptr« bezeichnet keinen Typ
    main.cpp:37: Fehler: »Mtx_ptr« bezeichnet keinen Typ
    main.cpp:44: Fehler: »Mtx_ptr« bezeichnet keinen Typ
    main.cpp: In constructor »D1::D1()«:
    main.cpp:32: Fehler: »mtx« wurde in diesem Gültigkeitsbereich nicht definiert
    main.cpp: In destructor »virtual D1::~D1()«:
    main.cpp:33: Fehler: »mtx« wurde in diesem Gültigkeitsbereich nicht definiert
    main.cpp: In member function »virtual void D1::foo()«:
    main.cpp:35: Fehler: »mtx« wurde in diesem Gültigkeitsbereich nicht definiert
    main.cpp:35: Fehler: »bar« wurde in diesem Gültigkeitsbereich nicht definiert
    main.cpp: At global scope:
    main.cpp:56: Fehler: »Mtx_ptr« bezeichnet keinen Typ
    main.cpp:63: Fehler: »Mtx_ptr« bezeichnet keinen Typ
    main.cpp: In constructor »D2::D2()«:
    main.cpp:49: Fehler: »mtx« wurde in diesem Gültigkeitsbereich nicht definiert
    main.cpp: In destructor »virtual D2::~D2()«:
    main.cpp:50: Fehler: »mtx« wurde in diesem Gültigkeitsbereich nicht definiert
    main.cpp: In member function »virtual void D2::foo()«:
    main.cpp:53: Fehler: »mtx« wurde in diesem Gültigkeitsbereich nicht definiert
    main.cpp:53: Fehler: »bar« wurde in diesem Gültigkeitsbereich nicht definiert
    


  • Du must noch den entsprechenden Header inkludieren:

    #include <tr1/memory>
    


  • joomoo schrieb:

    Du must noch den entsprechenden Header inkludieren:

    #include <tr1/memory>
    

    Unter Microsoft Visual C++ nur <memory> .

    Gibt es eigentlich Absichten, die Header des Technical Report 1 zu standardisieren, oder muss man als portabler TR1-Nutzer ständig mit bedingter Kompilierung arbeiten?


Anmelden zum Antworten