C++: delete auf Elternklasse

Gast #3772947
Lesenswert?

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:
1
elternklasse_t* eltklaptr;
2
if      (kindklassentyp = 0) { eltklaptr = new kindklasse_0_t; }
3
else if (kindklassentyp = 1) { eltklaptr = new kindklasse_1_t; }
4
else if (kindklassentyp = 2) { eltklaptr = new kindklasse_2_t; }
5
...

Dass Freigeben einer Kindklassen-Instanz würde ich gerne 
vereinheitlichen:
1
delete eltklaptr;

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?
#3772949
Lesenswert?

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 !
#3773009
Lesenswert?

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.
1
template<typename T> 
2
  T* constructor(){
3
    return new T();
4
  }
5
  typedef base*(*constructor_t)();
6
  const constructor_t constructors[] = {
7
    &constructor<derived1>,
8
    &constructor<derived2>,
9
    &constructor<derived3>
10
  };
11
  const std::size_t constructor_count = sizeof(constructors)/sizeof(constructor_t);
12
...
13
  base* eltklaptr = (*constructors[kindklassentyp])();
14
...

Bronco schrieb:
> Ist der Destructor der Elternklasse aber nicht virtuel, wird bei delete

Das ist undefiniert.
Gast #3773287
Lesenswert?

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.
Gast #3773527
Lesenswert?

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?
#3773535
Lesenswert?

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.
Gast #3774248
Lesenswert?

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?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren