Der Destruktor wird nicht aufgerufen



  • 9taleFox schrieb:

    ...jedoch besitzt der Vorfahre keinen Destruktor..

    Da täuschst Du Dich !
    JEDE Klasse besitzt einen Destruktor !
    Wenn Du selbst keinen deklarierst (in "class A { ....};"), dann erzeugt der Compiler Dir einen - und der ist nicht virtuell. Und das wiederum bedeutet, dass er nicht von dem einer abgeleiteten Klasse "overridden" wird !
    Wenn Du also willst, dass von Deiner Klasse abgeleitet wird, solltest/musst Du ihr einen virtuellen Dtor verpassen. Viele gute Programmierer sagen sogar, daß ein virtueller Dtor das Kennzeichen dafür ist, dass von dieser Klasse abgeleitet werden soll (bzw. bei Nichtvorhandensein: Nicht darf).....

    Bei "normalen Memberfunktionen" spielt es dabei übrigens erstmal keine Rolle, ob sie tatsächlich implementiert ist - Du brauchst nur eine Implementation, wenn sie auch aufgerufen wird.
    AAAABER: Ein Dtor wird IMMER aufgerufen (wenn ein entsprechendes Objekt instantiiert wird), deswegen braucht er auch immer eine Implementation (es sei denn, man will gerade darüber die Instantiierung verhindern).
    Und da eine Basisklasse innerhalb einer abgeleiteten Klasse auch instantiiert (und der Basis-Dtor automatisch aufgerufen) wird, kommt man da nicht drumherum.

    class BitteAbleiten {
    public:
        virtual ~BitteAbleiten() { std::cout << "~BitteAbleiten\n"; }
    };
    
    class GutAbgeleitet : public BitteAbleiten {
    public:
        ~GutAbgeleitet() { std::cout << "~GutAbgeleitet\n"; }
    };
    
    class BitteNichtAbleiten {
    public:
        ~BitteNichtAbleiten() { std::cout << "~BitteNichtAbleiten\n"; }
    };
    
    class SchlechtAbgeleitet : public BitteNichtAbleiten {
    public:
        ~SchlechtAbgeleitet() { std::cout << "~SchlechtAbgeleitet\n"; }
    };
    
    int main() {
        BitteAbleiten *gut = new GutAbgeleitet;
        BitteNichtAbleiten *schlecht = new SchlechtAbgeleitet;
    
        delete schlecht; // undefiniert; ruft bestenfalls nur ~BitteNichtAbleiten() auf
        delete gut; // ruft erst ~GutAbgeleitet() und dann ~BitteAbleiten() auf.
        return 0;
    }
    

    Kannst Du ja mal ausprobieren.....
    Das läuft auch identisch, wenn der BasisklassenDtor nur eine leere Implementation ("{}") hat - und genau DAS legt der Compiler für Dich an, wenn Du keinen eigenen Dtor deklarierst (aber leider nicht virtual).

    Gruß,

    Simon2.



  • Simon2 schrieb:

    AAAABER: Ein Dtor wird IMMER aufgerufen (wenn ein entsprechendes Objekt instantiiert wird), deswegen braucht er auch immer eine Implementation (es sei denn, man will gerade darüber die Instantiierung verhindern).
    Und da eine Basisklasse innerhalb einer abgeleiteten Klasse auch instantiiert (und der Basis-Dtor automatisch aufgerufen) wird, kommt man da nicht drumherum.

    Und selbst dann brauchst du eine Implementation für den Dtor - schließlich ruft der Dtor einer abgeleiteten Klasse automatisch den Basis-Dtor auf.

    (PS: Und neulich wurde ich belehrt, daß das delete schlecht in deinem Beispielcode als undefiniertes Verhalten gilt (auf Deutsch: das muß nichtmal den Basis-Dtor aufrufen))



  • CStoll schrieb:

    Simon2 schrieb:

    AAAABER: Ein Dtor wird IMMER aufgerufen (wenn ein entsprechendes Objekt instantiiert wird), deswegen braucht er auch immer eine Implementation (es sei denn, man will gerade darüber die Instantiierung verhindern).
    Und da eine Basisklasse innerhalb einer abgeleiteten Klasse auch instantiiert (und der Basis-Dtor automatisch aufgerufen) wird, kommt man da nicht drumherum.

    Und selbst dann brauchst du eine Implementation für den Dtor - schließlich ruft der Dtor einer abgeleiteten Klasse automatisch den Basis-Dtor auf....

    ... so wie ich im darauf folgenden Satz schrieb ... 😉

    Gruß,

    Simon2.



  • Ok, da habe ich mich schlecht ausgedrückt, mit “ ...jedoch besitzt der Vorfahre keinen Destruktor..“ habe ich gemeint, dass ich keinen definiert habe, dass einer vom Compiler erzeugt wird wusste ich auch, nicht aber, dass es nicht virtuell ist. Auch wenn es nicht virtuell ist so muss es aufgerufen werden, da der Destruktor des Erben aufgerufen wird und somit auch des Vorfahren. Das der Destruktor virtuell sein soll leuchtet mir ebenfalls ein.

    Hier der Ausschnitt in dem der Destruktor aufgerufen werden soll, das ist die einzige Stelle wo es geschieht:

    if (nackomen_Schriftart != NULL) {
    	HashtableIterator<OGLSchriftart>* hti = nackomen_Schriftart->items();
    	while (hti->hasMore()) {
    		OGLSchriftart* oglsa = hti->getNext();
    		delete oglsa;
    	}
    	delete hti;
    	delete nackomen_Schriftart;
    }
    

    Das dubiose ist eben, dass es die einzige Klasse dessen Speicher zwar freigegeben wird auch der Vorfahrklasse (wie ich es eben getestet habe), jedoch wird der Code im Destruktor nicht ausgeführt (der Vorfahrklasse schon).

    Der Destruktor:

    OGLSchriftart::~OGLSchriftart() {
    	HashtableIterator<OGLText>* oglti = texte->items();
    	while (oglti->hasMore())
    		((IEntferneSchriftart*)oglti->getNext())->entferneSchriftart();
    	delete oglti;
    	delete texte;
    	bild->nackomen_Schriftart->remove(name);
    }
    

    mfg.



  • Hi,

    mal eine Frage: Wie nutzt Du denn welchen Container ?
    STLContainer verwalten im allgemeinen Kopien, so dass Du für Polymorphie Zeiger in den Containern ablegen mußt ... ansonsten liegen da wirklich nur Basisobjekte.

    Ob's bei Dir so ist, weiß ich nicht, aber
    ...

    ...
    HashtableIterator<OGLSchriftart>...
    

    lässt mich daran denken.

    Gruß,

    Simon2.



  • Also der Container ist von mir geschrieben und hält nur Referenzen (die Objekte selbst ohne Kopien von dieser zu erzeugen).

    mfg.



  • Nur mal zur Orientierung: Ist 'OGLSchriftart' die Basisklasse oder die abgeleitete Klasse? Wenn ersteres, hängst du genau an dem Problem, das Simon oben beschrieben hat - da der Destruktor auf Basis des statischen Typs ausgewählt wird, erwischst du nur den Basis-Dtor, wenn du über einen Basis-Zeiger zugreifst.



  • Nein, das ist der 2 Fall:

    class SpeicherbaresObjekt {
    protected:
        string name;
    public:
        SpeicherbaresObjekt(string& name) {this->name = name;}
        virtual ~SpeicherbaresObjekt() {}  // nun jetzt auch virtuell
        string _name() {return name;}
    };
    
    class OGLSchriftart: public SpeicherbaresObjekt {
    //...
    public:
    //...
        virtual ~OGLSchriftart(); // nun jetzt auch virtuell
    //...
    };
    
    OGLSchriftart::~OGLSchriftart() {
    // der hier stehende Code wird nicht aufgeruffen
    }
    

    Wie gesagt ist das eben, das dubiose 😕

    mfg.



  • 😉

    Also ich denke, dass in dem Fall der Zeiger, auf den Du beim delete übergibst, tatsächlich auf eine Basisklasseninstanz zeigt.
    Entweder wirfst Du bereits ein "SpeicherbaresObjekt" in den Container oder der kopiert irgendwo doch implizit in ein solches....
    Schonmal den CopyCtor, operator=() und mögliche Konversionsoperatoren "privatisiert" ?

    Bei mir funktioniert jedenfalls auch der Umweg über "Basisklassen-Referenzen" prima:

    class BitteAbleiten {
    public:
        virtual ~BitteAbleiten() { std::cout << "~BitteAbleiten\n"; }
    };
    
    class GutAbgeleitet : public BitteAbleiten {
    public:
        ~GutAbgeleitet() { std::cout << "~GutAbgeleitet\n"; }
    };
    
    void deleteMe(BitteAbleiten* p) {
        delete p;
    }
    
    void doIt(BitteAbleiten& b) {
        deleteMe(&b);
    }	
    
    int main() {
        doIt(*new GutAbgeleitet);
        return 0;
    }
    

    Ergebnis wie erwartet:
    ~GutAbgeleitet
    ~BitteAbleiten

    Ein wenig überrascht mich, dass der Iterator (der Container auch ?) bei Dir für den Typen OGLSchriftart (also den abgeleiteten) instantiiert wird; eigentlich kenne ich das nur für Basistypen, wenn man Polymorphie nutzen will.... aber dazu weiß ich zuwenig von Deinem Container.

    Gruß,

    Simon2.



  • Nein, eben nicht wenn ich im Debugmodus bin und denn Code Schritt für Schritt durchgehe und mir die Variablen anschaue so zeigt oglsa auf eine Instanz von OGLSchriftart. In dem Programm habe ich noch einige andere Klassen die vom SpeicherbarenObekt erben und deren Destruktor auch aufgerufen wird und auch wenn es die Basisklasse währe so müsste der Destruktor der Abgeleiteten Klasse ebenfalls aufgerufen werden da ich dieses als virtual deklariert habe. Es ist halt die einzige Klasse die spinnt oder ich.

    Schonmal den CopyCtor, operator=() und mögliche Konversionsoperatoren "privatisiert" ?

    Nein.

    Ein wenig überrascht mich, dass der Iterator (der Container auch ?) bei Dir für den Typen OGLSchriftart (also den abgeleiteten) instantiiert wird; eigentlich kenne ich das nur für Basistypen, wenn man Polymorphie nutzen will.... aber dazu weiß ich zuwenig von Deinem Container.

    Ich iteriere den Container nur um die Instanzen zu entfernen.

    mfg.



  • 9taleFox schrieb:

    ...auch wenn es die Basisklasse währe so müsste der Destruktor der Abgeleiteten Klasse ebenfalls aufgerufen werden da ich dieses als virtual deklariert habe...

    Ich dachte da an so etwas (vgl. obiges Beispiel):

    int main() {
    	BitteAbleiten b = *new GutAbgeleitet; // nutzt CpyCtor
    	doIt(b);
    	b = *new GutAbgeleitet; // nutzt operator=()
    	doIt(b);
    	return 0;
    }
    

    oder noch unauffälliger:

    void doIt(BitteAbleiten b) { // Versehentlich call-by-value
        deleteMe(&b);
    }
    

    Sowas kann einem recht schnell und unbemerkt passieren.
    Solchen "Kopierfallen" kommt man mit privatisiertenCpyCtor, operator=(), Konversionsoperatoren und "explicit" auf die Schliche.....
    In all diesen Fällen zeigt letztlich der "zu deletende Zeiger" auf ein Basisklassenobjekt ... und da wird natürlich auch der BasisklassenDtor aufgerufen (wurde bei dem speziellen Objekt ja nicht overridden).
    (hier meine ich "Zeiger auf ein Basisklassenobjekt" und NICHT "Basisklassenzeiger auf ein Objekt")

    Ganz zu schweigen davon, dass man dabei unbemerkt ein delete auf ein Stackobjekt macht, was gaaaanz Pfuibäh ist .... aber verzeihlich, weil ja ein Fehler. 😉

    Gruß,

    Simon2.



  • Wenn es der Fall währe müsste es bei jeder anderen Klasse genau so en Problem geben, aber vielleicht sehe ich das Problem einfach nicht, hier ist der Code ... falls jemand zu viel zeit über hat :).

    Entwiklungsumgebung MSVC++ 2005 EE

    mfg



  • Hopps,

    das ist aber ein Brocken für "mal eben" (zumal ich keine MSVC++ 2005 EE) habe ....

    Du bist aber vermutlich sicher, dass der Dtor nicht aufgerufen wird (so mit Ausgabe ausprobiert und so) ?!?

    Gruß,

    Simon2.



  • Beim Compilieren erhälst Du eine Warnmeldung, das der Typ OLGSchriftArt nicht bekannt ist. Includiere den Header, damit der Compiler weiß wie die Klasse aussieht.

    Warnung 15 warning C4150: Löschen eines Zeigers auf den nicht definierten Typ 'OGLSchriftart'. Destruktor wurde nicht aufgerufen. c:\dokumente und einstellungen\mroth\desktop\openglwindow_test2\openglwindow_test2\oglbild.cpp 161

    Warnungen sind zum lesen da, nicht zum Ignorieren 😉



  • OK, ich habe einen Verdacht, galube das du recht haben könntest bin mir aber noch nicht sicher. Die Warnung wurde bei mir nicht angezeigt, aber das liegt wohl an dem Container.



  • Ich bin mir schon sicher



  • Knuddlbaer schrieb:

    Beim Compilieren erhälst Du eine Warnmeldung, das der Typ OLGSchriftArt nicht bekannt ist. Includiere den Header, damit der Compiler weiß wie die Klasse aussieht.

    Warnung 15 warning C4150: Löschen eines Zeigers auf den nicht definierten Typ 'OGLSchriftart'. Destruktor wurde nicht aufgerufen. c:\dokumente und einstellungen\mroth\desktop\openglwindow_test2\openglwindow_test2\oglbild.cpp 161

    Warnungen sind zum lesen da, nicht zum Ignorieren 😉

    😮 😮 😮
    Das ist eine Warnung ???
    Ich hätte das als Grund für einen echten Fehler gehalten....

    Gruß,

    Simon2.

    P.S.: Mich hatte schon ein wenig irritiert, dass in Bild plötzlich nochmal Schriftart-Verwaltung auftauchte, aber meine Energie reichte nicht weiter....



  • OK das habe ich ganz übersehen, wenn da nur

    class OGLSchriftart;
    

    steht, sollte man sich eigentlich nicht wundern, wenn man zulange davor sitzt sieht man irgendwann überhaut nichts mehr, ich danke euch vielmals!!!

    P.S.: Mich hatte schon ein wenig irritiert, dass in Bild plötzlich nochmal Schriftart-Verwaltung auftauchte, aber meine Energie reichte nicht weiter....

    Wenn die Textur nicht mehr existiert auf der, die Schriftart definiert ist, sollte auch die Schriftart nicht mehr geben, war der Gedanke.

    mfg.



  • 9taleFox schrieb:

    ...
    Wenn die Textur nicht mehr existiert auf der, die Schriftart definiert ist, sollte auch die Schriftart nicht mehr geben, war der Gedanke.

    mfg.

    Klingt logisch !

    Aber mal eine Frage: Wie kommt es, dass Du die Warning nicht gesehen hast ? Hast Du die abgeklemmt ?

    Gruß,

    Simon2.



  • Wenn man ständig mit warnungen wie,

    warning C4996: 'fopen' wurde als veraltet deklariert oder
    'Initialisierung': Konvertierung von 'double' in 'int', möglicher Datenverlust
    

    geplagt wird ,die ersteinmal nicht so wichtig sind, neigt man dazu diese zu ignorieren, tja und schwups und man ignoriert welche die wichtig sind, also praktisch nicht angezeigt ;).

    mfg


Anmelden zum Antworten