Löschen von polymorphen Objekten mit delete
-
Solange du keinen virtuellen destructor in derBasisklasse definiert hast, gibt delete nur soviel Speicher frei, wie die Basisklasse belegt. Das heißt es wird mit jedem Objekt, was du anlegst memory leaks erzeugt, da new derived sizeof(derived) bytes alloziert aber delete (base*)derived nur sizeof(base) bytes freigibt --> memleak of sizeof(derived)-sizeof(base)
-
SeppJ schrieb:
Das bedeuted also, dass ich, wenn ich auf Nummer sicher gehen will, also vorher eine Typenidentifizierung machen muss?
Wie jetzt schon mehrfach erwähnt reicht ein virtueller Destruktor in der Basisklasse.
In deinen einfachen Fall sähe der so aus:
class base{ int a; virtual ~base() {}; };Da kein dynamisch allozierter Speicher im Objekt ist, reicht der leere Destruktor, er muss aber virtuell sein.
cu André
-
vlad_tepesch schrieb:
Solange du keinen virtuellen destructor in derBasisklasse definiert hast, gibt delete nur soviel Speicher frei, wie die Basisklasse belegt. Das heißt es wird mit jedem Objekt, was du anlegst memory leaks erzeugt, da new derived sizeof(derived) bytes alloziert aber delete (base*)derived nur sizeof(base) bytes freigibt --> memleak of sizeof(derived)-sizeof(base)
Falsch!
Eine Implementierung darf das von dir beschriebene Verhalten haben, muss es aber nicht.
Wie schon erwähnt wurde gibt es viele Implementierungen die es eben genau nicht so machen wie du beschreibst, z.B. weil sie einfach auf malloc/free aufsetzen (und free eben "selbst weiss" wie gross der angeforderte Block war).Warum du, nachdem das eigentlich schon geklärt war, jetzt nochmal Unfug postest, ist mir irgendwie nicht ganz klar.
-
hustbaer schrieb:
Warum du, nachdem das eigentlich schon geklärt war, jetzt nochmal Unfug postest, ist mir irgendwie nicht ganz klar.
Ich hab das gepostet, da keiner der Vorredner das Erzeugen von memory leaks erwähnt hat.
Auch weiß ich, dass der vc++ an so einer Stelle Speicherlecks erzeugt.
-
vlad_tepesch schrieb:
Auch weiß ich, dass der vc++ an so einer Stelle Speicherlecks erzeugt.
Es gibt nicht "den VC++". Wenn du eine spezifische Version meinst, dann solltest du die nennen. Der aktuelle (2008) erzeugt keine Speicherlecks.
-
Sorry, ich spezifiziere:
der vc++ 8 erzeugt lecks. Deswegen warhscheinlich auch ältere Versionen, mit der 9 hab ich noch nicht gearbeitet.Edit:
Auf jeden Fall entstehen dann Lecks, wenn man in der Abgeleiteten Klasse Speicher alloziert, der natürlich im Destruktor der abgeleiteten Klasse wieder freigegeben wird.Hat man keinen virutellen Basis-Destructor wird der abgeleitete nicht aufgerufen und man hat sein leck.
Deswegen sollte man bei Basisklasse generell einen virtuellen Destructor vorsehen, außer es wird eine kleine Klasse, wo es auf effizienz ankommt und von der nicht abgeleitet wird.
-
vlad_tepesch schrieb:
Sorry, ich spezifiziere:
der vc++ 8 erzeugt lecks.Kann ich nicht reproduzieren. Zeig mal ein Testprogramm.
-
MFK schrieb:
vlad_tepesch schrieb:
Sorry, ich spezifiziere:
der vc++ 8 erzeugt lecks.Kann ich nicht reproduzieren. Zeig mal ein Testprogramm.
Ich hatte mit sowas mal zu kämpfen und habs recht lange gesucht, bis mir aufgefallen ist, dass ich bei der Basisklasse das virtuell vorm Destructor vergessen habe.
Aber ihr habt recht
, wahrscheinlich lag es eher an der sache, die ich grad noch meinem vorigen Post hinzugefügt habe.
Dies Unterstützt aber eher noch die Aussage, dass man bei umfangreichen Klassenhierarchien den basisdestruktor generell virtuell machen sollte.
-
vlad_tepesch schrieb:
Aber ihr habt recht
, wahrscheinlich lag es eher an der sache, die ich grad noch meinem vorigen Post hinzugefügt habe.Ja, dann ist das klar.
vlad_tepesch schrieb:
Dies Unterstützt aber eher noch die Aussage, dass man bei umfangreichen Klassenhierarchien den basisdestruktor generell virtuell machen sollte.
Ich bleibe lieber bei: (public und virtual) oder (protected und nicht virtual).
-
Der aktuelle (2008) erzeugt keine Speicherlecks.
Der weniger aktuelle (2005) erzeugt ebenfalls keine Speicherlecks.
Ich bleibe lieber bei: (public und virtual) oder (protected und nicht virtual).
Warum sollte man in Klassenhirarchien protected destruktoren nicht virtuell machen ???
Beisst sich mit dem was ich eingetrichtert bekommen hab irgendwie !protected destructor = man kann die klasse nur ueber den abgeleiteten zeiger zerstoeren ! Von innern heraus wuerde zwar auch gehen (destroy methode) aber dann wuerde man den destructor eher privat machen.
nicht virtueller destructor = hier will mir der Designer der klasse sagen, das diese klasse eher nicht zum ableiten vorgesehen ist ....
Wenn ich wissen will, ob ich von ner klasse ableiten kann, schau ich immer zuerst, ob der destruktor virtuell ist. In einigen c++ buechern wird das auch so vertreten ....oder spricht was gegen virtuelle protected destructoren ???
Ciao ...
-
RHBaum schrieb:
protected destructor = man kann die klasse nur ueber den abgeleiteten zeiger zerstoeren !
Das ist gerade der Sinn in dem Fall. Entweder ein virtueller Destruktor (public) in der Basisklasse, dann kann man auch über den Basiszeiger löschen. Oder ein nicht öffentlicher Destruktor (protected würde mir hier schon einleuchten) und so verhindern das man über die Basisklasse löschen kann.
RHBaum schrieb:
Wenn ich wissen will, ob ich von ner klasse ableiten kann, schau ich immer zuerst, ob der destruktor virtuell ist. In einigen c++ buechern wird das auch so vertreten ....
Das ist auch der Fall, den ich in der Regel vertrete. Dennoch ist dies keine Festgeschriebene Regel. Man sollte aber immer entweder einen virtuellen Destruktor verwenden oder aber das Löschen über die Basisklasse verbieten.
In etwas tieferen Ableitungshirachien (die man aber im Regelfall aber ohnehin meiden sollte) sehe ich bei letzteren aber die Gefahr das man dann in abgeleiten Klassen, die aber selbst über Ableitungen verfügen, das virtual vergisst wenn es relevant ist.
cu André
-
Ich versuche wenn möglich den Nutzen einer jeden Klasse über ihren Namen bzw. ihren Destruktor kenntlich zu machen. Der Name kommt dabei hauptsächlich ins Spiel, wenns sich um irgendwelche Policies, Traits, Funktoren etc. handelt. Bei Ausgewachsenen Klassen (fast ausschließlich nicht-templates) gibts im grunde drei Kategorien, die man grundsätzlich über ihren Dtor kenntlich machen kann:
- Basisklassen in polymorphen Hierarchien -> pure virtual Dtor
- Basisklassen in nichtpolymorphen Hierarchien -> protected Dtor
- Konkrete Klassen -> public DtorNatürlich ist das nur ein Idealbild, aber imho sollte man schon ein wenig Hirnschmalz investieren, wenn man mal nicht drum herumkommt das anders zu machen, und das dann vor allem Dokumentieren.
-
Basisklassen in nichtpolymorphen Hierarchien -> protected Dtor
Du verwendest also Vererbung(wegen protected) zu einem anderem zwecke als Polymorphie ?
Das heisst du schreibst also Libs /Frameworks, wo man nur ueber vererbung an die funktionalitaet rankommt, und aggregation in den faellen damit ausgschlossen ist.
Oder hab ich das falsch verstanden ?Ich vermeide das eigentlich immer, in vielen Buechern steht auch, das man besser Aggregation als private Vererbung als Mittel fuer "ist implementiert mit" Beziehungen verwenden sollte. Quasi man sollt es nur einsetzen wo es noetig ist ... aber wo ist es noetig, wenn man selber der Autor der Klasse mit der basisfunktionalitaet ist ?
Das ist auch der Fall, den ich in der Regel vertrete. Dennoch ist dies keine Festgeschriebene Regel.
Jein, ist das aber ned schon eine Stil Frage ?
Ich verwend auch protected destructoren, genau fuer obigen Zweck, klar. Aber ich mach die dinger IMMER virtuell. Es gibt IMHO eigentlich keinen Grund der dagegen spricht, es sei denn man vergissts einfach
Man muss den destruktor eh implementieren, damit er protected wird, dann kann man das virtual auch davorschreiben.
Klar hats eiegntlich nur symbolischen character, weil technisch auf die Nase kann man ned fallen, wenn die abgeleitete klasse korrekt implementiert ist (virtual DTor) und den basiszeiger kann man eh ned deleten.Ciao ...
-
RHBaum schrieb:
Du verwendest also Vererbung(wegen protected) zu einem anderem zwecke als Polymorphie ?
In der generischen Programmierung gang und gaebe.
C++ fehlt leider eine static vererbung die verbietet die Klasse polymorph zu verwenden. Deshalb muss man oeffentlich erben.ein aktuelles Beispiel ist zB habe ich eine klasse fuer sortierte container:
template<typename ContainerT> class sorted_container : public interface_add_sorted_insert< interface_add_const_sequence_access< interface_base< ContainerT > > > { //... };Ueber Vererbung wird das Interface der Klasse bestimmt. Das doofe, jeder kann nun die Klasse zB als interface_base<T> ansprechen - mit static Vererbung waere das nicht passiert.
Aber man hat in C++ da leider keine andere moeglichkeit. idR ist das aber auch kein Problem, da es keinen Sinn macht diese Klasse polymorph zu verwenden.
-
Versteh ich ehrlich gesagt ned 100%ig
Warum brauchst du das Interface, wenn nicht fuer Polymorphie ?
Nur um die reine Schnittstelle festzuschreiben ???aber wenn niemals eine andere interface_add_sorted_insert<"template geroedel"> Auspraegung hasst, brauchst du doch auch keine LaufzeitPolymorphie, ergo du koenntest die Schnittstelle durch das verwendende Template festschreiben ?
Warum geht sowas ned ueber normale Policies etc zu loesen ?
Ciao ...
-
das sind vermutlich keine interfaces sondern templates die eine oder mehrere funktionen zum public interface hinzufügen bzw. überschreiben/überdecken. lies "interface-add: blah", nicht "interface, add-blah".