virtueller destruktor



  • lol_of_lolcraft schrieb:

    man soll ja nur von klassen erben die einen virtuellen dtor haben.

    Ist nur die halbe Wahrheit. Virtueller Destruktor, wenn du über einen Basisklassenzeiger zerstören willst. Sonst protected und nichtvirtuell, wenn die Klasse ausschließlich als Basisklasse verwendet werden soll.

    wenn ich nun in base selber keinen dtor deklariere, wird der compiler mir einen virtuellen hinzufügen?

    Nein. Der Compiler macht nichts von selbst virtuell, was nicht schon vorher (d.h. in einer Elternklasse) virtuell war.



  • pumuckl schrieb:

    wenn ich nun in base selber keinen dtor deklariere, wird der compiler mir einen virtuellen hinzufügen?

    Nein. Der Compiler macht nichts von selbst virtuell, was nicht schon vorher (d.h. in einer Elternklasse) virtuell war.

    Stimmt, sry, nicht genau genug gelesen. Wenn die Basis einen virtuellen Dtor hat dann mach der Compiler auch für jede abgeleitete Klasse autom. einen virtuellen Dtor. Aber wenn die Basis nicht explizit einen virtuellen Dtor deklariert dann hat sie auch keinen...



  • lol_of_lolcraft schrieb:

    man soll ja nur von klassen erben die einen virtuellen dtor haben.

    Das stimmt so nicht. Aber man soll nur Klassen polymorph verwenden, die einen virtuellen Destruktor haben.

    Vererbung impliziert keine Polymorphie.



  • Nexus schrieb:

    lol_of_lolcraft schrieb:

    man soll ja nur von klassen erben die einen virtuellen dtor haben.

    Das stimmt so nicht. Aber man soll nur Klassen polymorph verwenden, die einen virtuellen Destruktor haben.

    Naja, das stimmt so auch nicht.

    Es geht einfach nur darum ob Objekte jemals über einen Basisklassenzeiger gelöscht werden.



  • hustbaer schrieb:

    Nexus schrieb:

    lol_of_lolcraft schrieb:

    man soll ja nur von klassen erben die einen virtuellen dtor haben.

    Das stimmt so nicht. Aber man soll nur Klassen polymorph verwenden, die einen virtuellen Destruktor haben.

    Naja, das stimmt so auch nicht.

    Es geht einfach nur darum ob Objekte jemals über einen Basisklassenzeiger gelöscht werden.

    Richtig. Es gibt (wenige) Situationen, wo man beim Erzeugen/Zerstören sehr genau weiß, mit welcher konkreten Klasse man es zu tun hat (d.h. kein virtueller Dtor nötig), wo aber Referenzen auf die Basisklasse durchs Programm geworfen werden inclusive Aufrufen von virtuellen Methoden. Einige Compiler meckern dann fleißig wenn der Dtor nicht virtuell ist 😕



  • hustbaer, ich habe mir vorher ernsthaft überlegt, das auch noch zu schreiben, fand es dann aber zu unwichtig.

    Meine Überlegung: Grundsätzlich treffe ich beim Design einer Klasse die Entscheidung, ob sie polymorph ist, also virtuelle Funktionen anbietet oder nicht. Sobald einige Funktionen virtuell sind, kann ich den Destruktor auch gleich virtuell machen, da

    1. der Benutzer im Allgemeinen davon ausgeht, dass man polymorphe Klassen auch polymorph zerstören kann
    2. der Overhead der VTable ohnehin schon da ist.

    Bei Vererbung hingegen ist es ein viel grösserer Schritt bis zur polymorphen Zerstörung, daher halte ich es für sinnvoll, hier die Grenze zu ziehen. Aber es gibt natürlich Situationen, wo man den virtuellen Destruktor nicht zwingend braucht (z.B. auch bei shared_ptr ).



  • Nexus schrieb:

    1. der Benutzer im Allgemeinen davon ausgeht, dass man polymorphe Klassen auch polymorph zerstören kann

    Sehe ich anders. Vor allem wenns um Factories geht, wo im sauberen Fall die Objekte dort zerstört werden, wo sie erzeugt werden, nämlich in der factory.

    1. der Overhead der VTable ohnehin schon da ist.

    Die vtable ist eine Speicher-Angelegenheit. Der Aufruf von virtuellen Funktionen über die vtable eine Performance-Angelegenheit.



  • pumuckl schrieb:

    Sehe ich anders. Vor allem wenns um Factories geht, wo im sauberen Fall die Objekte dort zerstört werden, wo sie erzeugt werden, nämlich in der factory.

    Ich schreibe ja "im Allgemeinen". Dabei denke ich zunächst an Klassen, die vom Benutzer selbst erzeugt und zerstört werden. Bei Factories ist der Destruktor je nachdem gar nicht öffentlich...

    Abgesehen davon finde ich es generell sauberer, wenn das Objekt nicht explizit gelöscht werden muss, z.B. indem es in einem Smart-Pointer gespeichert wird. Im konkreten Fall hängt es halt von den genauen Anforderungen (Besitzsemantik, Performance etc.) ab, was man nimmt.

    pumuckl schrieb:

    Die vtable ist eine Speicher-Angelegenheit. Der Aufruf von virtuellen Funktionen über die vtable eine Performance-Angelegenheit.

    Gut, aber was hat das mit meiner Aussage zu tun? Ich meinte damit nur, dass ich durch die Regel "alle Basisklassen müssen virtuelle Destruktoren haben" unnötig VTable-Overhead erzeuge, während die Nachteile bei "alle polymorphen Klassen müssen virtuelle Destruktoren haben" 1. weniger dramatisch ausfallen und 2. durch die höhere Wahrscheinlichkeit, dass der Destruktor polymorph aufgerufen wird, eher gerechtfertigt sind.



  • Sobald eine Methode virtuell ist, ist sie zum Ableiten gedacht und dann mache ich automatisch den Dtor virtuell. Fertig.



  • Ja das ist eine gängige Faustregel. Aber wie schon gesagt wurde ist ein virtueller Destruktor nicht immer notwendig, auch wenn die Klasse eine Basis ist und virtuelle Methoden hat. Ein virtueller Destruktor ist nur dann notwendig wenn über einen Basisklassenzeiger deleted werden soll. Aber zumindest besser als andere Faustregeln wo z.B. propagiert wird dass jede Klasse immer einen virtuellen Dtor braucht (meist mit der Begründung dass ja irgendwer irgendwann davon ableiten könnte), was natürlich vollkommener Schwachsinn ist und viel Schaden anrichten kann.



  • Woher sollte ich denn wissen, dass die Instanzen von abgeleiteten Klassen absolut niemals ueber Basisklassenzeiger geloescht werden?

    Das kann ich als Erzeuger der Basisklasse doch meistens garnicht wissen.



  • this->that schrieb:

    Das kann ich als Erzeuger der Basisklasse doch meistens garnicht wissen.

    Doch. Wenn du eine Basisklasse schreibst dann weißt du doch für was die gedacht ist, immerhin bist du dort auf der selben Abstraktionsschicht wie der Code der sie verwendet (wenn nicht dann verwendest du Vererbung grad falsch). Und in den entsprechenden Fällen wirst du den Destruktor protected machen, damit ist es unmöglich solche Objekte über einen Basisklassenzeiger zu deleten...



  • dot schrieb:

    this->that schrieb:

    Das kann ich als Erzeuger der Basisklasse doch meistens garnicht wissen.

    Doch. Wenn du eine Basisklasse schreibst dann weißt du doch für was die gedacht ist

    Ja. Zum vererben. 😃

    dot schrieb:

    immerhin bist du dort auf der selben Abstraktionsschicht wie der Code der sie verwendet (wenn nicht dann verwendest du Vererbung grad falsch). Und in den entsprechenden Fällen wirst du den Destruktor protected machen, damit ist es unmöglich Objekte über einen Basisklassenzeiger zu deleten...

    Habe ich in der Praxis noch nicht ein einziges mal gesehen. In 99% der Faelle schreibst du virtual, weil das Ding ne Basisklasse wird und andere davon ableiten sollen und es nahezu immer Sinn macht, die Moeglichkeit einzuraeumen ueber Basisklassenzeiger zu loeschen.



  • this->that schrieb:

    dot schrieb:

    this->that schrieb:

    Das kann ich als Erzeuger der Basisklasse doch meistens garnicht wissen.

    Doch. Wenn du eine Basisklasse schreibst dann weißt du doch für was die gedacht ist

    Ja. Zum vererben. 😃

    Nur ein verschwindend geringer (man könnte fast sagen vernachlässigbarer) Teil meiner "Basisklassen" dient wirklich als Basisklasse in dem Sinne dass andere Klassen etwas davon erben (ich bin mit solche Klassen bewusst sehr sparsam). Der Großteil sind einfach nur rein abstrakte Interfaces. Der Code der diese Interfaces verwendet ist vollkommen losgelöst vom Code der die Objekte erzeugt die diese Interfaces implementieren. Der Code der die Objekte erzeugt kennt die aber meistens direkt und nicht nur über ihr Interface. Die Möglichkeit Objekte über Basisklassenzeiger zu deleten braucht man gar nicht so oft wie du vielleicht denkst und wenn dann beschränkt sich das auch auf kleine Bereiche des Codes.



  • dot schrieb:

    ein virtueller Destruktor nicht immer notwendig, auch wenn die Klasse eine Basis ist und virtuelle Methoden hat. Ein virtueller Destruktor ist nur dann notwendig wenn über einen Basisklassenzeiger deleted werden soll.

    Ich verstehe nicht, wozu die Basis virtuelle Funktionen hat, wenn Sie sowie nicht über einen Basiszeiger angesprochen werden kann.



  • dot schrieb:

    this->that schrieb:

    dot schrieb:

    this->that schrieb:

    Das kann ich als Erzeuger der Basisklasse doch meistens garnicht wissen.

    Doch. Wenn du eine Basisklasse schreibst dann weißt du doch für was die gedacht ist

    Ja. Zum vererben. 😃

    Nur ein verschwindend geringer (man könnte fast sagen vernachlässigbarer) Teil meiner "Basisklassen" dient wirklich als Basisklasse in dem Sinne dass andere Klassen etwas davon erben (ich bin mit solche Klassen bewusst sehr sparsam). Der Großteil sind einfach nur rein abstrakte Interfaces. Der Code der diese Interfaces verwendet ist vollkommen losgelöst vom Code der die Objekte erzeugt die diese Interfaces implementieren. Der Code der die Objekte erzeugt kennt die aber meistens direkt und nicht nur über ihr Interface. Die Möglichkeit Objekte über Basisklassenzeiger zu deleten braucht man gar nicht so oft wie du vielleicht denkst und wenn dann beschränkt sich das auch auf kleine Bereiche des Codes.

    Naja, letztlich braucht man es nur dann nicht, wenn die Erzeugung und Zerstoerung zentralisiert (z.B. ueber eine Factory) stattfindet.

    Es ist auf jeden Fall nicht der Standardfall, denn das ist ja gerade einer DER Pluspunkte von Vererbung, dass du gegen ein Interface programmierst und nicht wissen musst wie die Implementierung aussieht und auch das Loeschen automatisch klappt.

    Ich bleibe auf jeden Fall bei meiner Faustregel=)



  • Jockelx schrieb:

    Ich verstehe nicht, wozu die Basis virtuelle Funktionen hat, wenn Sie sowie nicht über einen Basiszeiger angesprochen werden kann.

    "Ansprechen" ist was andres als das Objekt deleten...



  • this->that schrieb:

    Es ist auf jeden Fall nicht der Standardfall, denn das ist ja gerade einer DER Pluspunkte von Vererbung, dass du gegen ein Interface programmierst und nicht wissen musst wie die Implementierung aussieht [...]

    Naürlich. Aber wie gesagt: Der Code der das abstrakte Interface verwendet ist normalerweise losgelöst vom Code der die Objekte erzeugt und zerstört.

    this->that schrieb:

    Naja, letztlich braucht man es nur dann nicht, wenn die Erzeugung und Zerstoerung zentralisiert (z.B. ueber eine Factory) stattfindet.

    Beispiel:

    Ich hab mein 3D Spiel und da hab ich eine Klasse Scene die eben meine 3D Szene darstellt. Und in diese Szene kann ich jetzt SceneObject s (ein Interface) einfügen. Nun erzeugt meine Spiellogik Objekte die auch eine graphische Repräsentation haben sollen. Diese Objekte erzeugen (oder bekommen) ihrerseits die entsprechenden Grafikobjekte die alle SceneObject s sind und dann in die Scene eingefügt werden. Aber meine Klasse Player weiß dass sie ein AnimatedModel hat und kennt dieses nicht nur als SceneObject . Anders gehts sogar garnicht da die Player Klasse ja die Animationen abspielen muss und dafür reicht ein allgemeines SceneObject nicht. Wer auch immer das AnimatedModel erstellt zerstört es auch wieder und tut dies über den konkreten Typ und nicht über das Interface. Wenn man mit Dependency Injection arbeitet ergeben sich meiner Erfahrung nach eigentlich immer solche Strukturen. Das hat nichts mit zentralen Factories zu tun sondern einzig mit der Tatsache dass man den konkreten Typ kennen muss um ein Objekt zu erzeugen. Natürlich gibt es auch Fälle wo man über Basiszeiger deleten will, aber die sind meiner Erfahrung nach bei weitem nicht so häufig wie man vielleicht denken mag.



  • Hilft mir ehrlich gesagt nicht:

    SceneObject ist jetzt die Basisklasse mit virtuellen Funktionen und protected Destruktor?
    Und AnimatedModel ist eine Ableitung davon?
    Und was ist jetzt der Vorteil davon (statt 'normalem' virtuellen Destruktor)?



  • Jockelx schrieb:

    SceneObject ist jetzt die Basisklasse mit virtuellen Funktionen und protected Destruktor?

    ja z.B.

    Jockelx schrieb:

    Und AnimatedModel ist eine Ableitung davon?

    ja

    Jockelx schrieb:

    Und was ist jetzt der Vorteil davon (statt 'normalem' virtuellen Destruktor)?

    Es geht nicht um irgendeinen Vorteil sondern darum zu demonstrieren dass hier nirgendwo über Basiszeiger deleted werden muss. Der der das Objekt erzeugt hat zerstört es auch wieder und er kennt das Objekt beim konkreten Typ und nicht nur über den Basiszeiger. Und das Beispiel ist jetzt nicht gerade an den Haaren herbeigezogen, sowas hat man eigentlich ziemlich oft. Gerade eine Factory ist eigentlich die einzige Möglichkeit Objekte zu erzeugen ohne ihren konkreten Typ zu kennen.


Anmelden zum Antworten