Hallo zusammen,
ich habe eine Elternklasse, von der ich mehrere Kindklassen ableite.
Die Kindklassen haben alle eine unterschiedliche Anzahl an Variablen,
d.h. ihre Instanzen belegen unterschiedlich viel RAM.
Mein Wunsch ist es, der Einfachheit halber möglichst oft über einen
Elternklassen-Pointer auf die Instanzen der unterschiedlichen
Kindklassen zuzugreifen.
Beim Anlegen einer Kindklassen-Instanz muss ich eine
Einzelfallbehandlung machen:
Dass Freigeben einer Kindklassen-Instanz würde ich gerne
vereinheitlichen:
1
deleteeltklaptr;
Nun habe ich im Netz folgendes gefunden:
Ist der Destructor der Elternklasse virtuel, wird bei delete mit
Elternklassenzugriff die komplette Instanz (Elternteil und Kindteil)
freigegeben.
Ist der Destructor der Elternklasse aber nicht virtuel, wird bei delete
mit Elternklassenzugriff nur der Elternteil der Instanz freigegeben.
Das würde ja bedeutet, dass der Kindteil der Instanz als Speicherleiche
zurückbleibt. Ist das richtig?
Nicht ganz. Wahrscheinlich wird durch die Abbildung auf den
zugrundeliegenden malloc()/free() Mechanismus aller Speicher
freigegeben.
Problem ist aber das die Destructoren der Daten der Kind-Objekte nicht
aufgerufen werden. Das kann unterschiedliche Auswirkungen haben - gut
ist das auf jeden Fall nicht !
Bronco schrieb:> if (kindklassentyp = 0) { eltklaptr = new kindklasse_0_t; }> else if (kindklassentyp = 1) { eltklaptr = new kindklasse_1_t; }
Vorsicht! der vergleichsoperator ist == und nicht =
Für soetwas würde ich eher arrays oder const std::map nehmen.
Bronco schrieb:> Nun habe ich im Netz folgendes gefunden:> ...
Deine Fundstelle im Netz ist da ungenau. Merk dir einfach anders herum:
Willst du delete auf abgeleitete Klassen über einen Pointer auf die
Basisklasse ausführen, muß der Destruktor der Basisklasse virtuell sein.
Einfach machen, und alles wird gut.
Daniel A. schrieb:> Vorsicht! der vergleichsoperator ist == und nicht => Für soetwas würde ich eher arrays oder const std::map nehmen.
So was nennt sich factory, und so würde ich as auch im Code nennen, und
nicht "constructor", denn ein C ++ konstruktor ist es nicht! Und statt
Funktionspointern kann man das mit std::function schöner/universeller
machen.
Danke für die Antworten:
Was mich verwirrt hatte:
http://stackoverflow.com/questions/461203/when-to-use-virtual-destructors
---------8<---------------
Since Base's destructor is not virtual and b is a Base* pointing to a
Derived object, delete b has undefined behaviour. In most
implementations, the call to the destructor will be resolved like any
non-virtual code, meaning that the destructor of the base class will be
called but not the one of the derived class, resulting in resources
leak.
---------8<---------------
Hier wird gesagt, dass das Nicht-Aufrufen des Kindklassen-Destructor zu
"ressource leak" führt.
Ist logisch für den Fall, wo im Kindklassen-Destructor explizit
Ressourcen freigegeben werden.
Aber auf den mit new reservierten Speicherplatz für die
Kindklassen-Instanz, der nun mit delete wieder freigeben wird, hat das
doch keinen Einfluss, oder?
Programmierer schrieb:> denn ein C ++ konstruktor ist es nicht!
So ist es. So braucht man ja nicht mal mehr Vererbung.
Aber das ist ja das schöne an C++. Jeder, wie er will. Der eine machts
old-school, der andere baut Templates.
Oliver
Bronco schrieb:> Hier wird gesagt, dass das Nicht-Aufrufen des Kindklassen-Destructor zu> "ressource leak" führt.
Es wird gesagt, daß nicht definiert ist, was passiert. Das ist der
wichtige Teil. Als Randnotiz wird noch erwähnt, daß es auf den meisten
Compilern dazu führen wird, daß nur der Destruktor der Basisklasse
aufgerufen wird.
> Ist logisch für den Fall, wo im Kindklassen-Destructor explizit> Ressourcen freigegeben werden.
Es passiert vermutlich auch in dem Fall, indem die Kindklasse wieder
Objekte mit Destruktoren enthält, die beim Zerstören eines Objekts der
Kindklasse implizit mit aufgerufen werden.
> Aber auf den mit new reservierten Speicherplatz für die> Kindklassen-Instanz, der nun mit delete wieder freigeben wird, hat das> doch keinen Einfluss, oder?
Spielt eigentlich keine Rolle. Wie gesagt: Das wichtige ist, daß das
Verhalten undefiniert ist und damit erstens vom Compiler (und notfalls
auch der Mondphase) abhängig sein kann und zweitens grundsätzlich zu
vermeiden ist.
Rolf Magnus schrieb:> Es wird gesagt, daß nicht definiert ist, was passiert. Das ist der> wichtige Teil.
Okay, verstanden.
Dann bin ich aber echt froh, dass der GCC eine Warnung ausgibt, denn von
allein wäre ich da nicht drauf gekommen.
Oliver S. schrieb:> So ist es. So braucht man ja nicht mal mehr Vererbung.
Doch, braucht man wohl. Die Klassen, von denen Instanzen angelegt werden
sollen, müssen eine gemeinsame Basisklasse haben, die dann als
Rückgabetyp der Factory-Funktionen verwendet wird. Was willst du als
Rückgabetyp verwenden, wenn es keine gemeinsame Basisklasse gibt? Und
wie virtuelle Funktionen aufrufen?
Oliver S. schrieb:> Das ist ja das "schöne" an Templates. Lauter völlig unabhängige Klassen,> die Zillionen Kombinationsmöglichkeiten verwaltet der Compiler.
Das bringt dir leider bei Factory-Funktionen gar nichts. Gib mir mal ein
konkretes Code-Beispiel wie du das hier ohne Vererbung hinbekommen
willst:
https://ideone.com/h2xEbm