mikrocontroller.net

Forum: PC-Programmierung C++ virtual destructor


Autor: Maier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Im Vererbungskontext kann es Sinn machen Virtual vor den Destructor zu 
schreiben.

Ich frage mich gerade ob es irgendwelche Nachteile hätte allgemein vor 
jeden Destruktor ein Virtual zu setzen?

Autor: Noch einer (Gast)
Datum:

Bewertung
3 lesenswert
nicht lesenswert
Höherer Speicherbedarf, mehr Laufzeit.

Wenn auch nur eine einzige virtuelle Methode existiert, legt der 
Compiler den ganzen Verwaltungsaufwand für virtuelle Methoden an.

Autor: fauler Sack (Gast)
Datum:

Bewertung
-3 lesenswert
nicht lesenswert
Ja, bei virtual musst du ihn in jedem Fall überschreiben.

Autor: jz23 (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
fauler Sack schrieb:
> Ja, bei virtual musst du ihn in jedem Fall überschreiben.

Verwechselt du gerade virtual und abstrakt? Virtual muss man nicht 
überschreiben.

Autor: fauler Sack (Gast)
Datum:

Bewertung
-4 lesenswert
nicht lesenswert
VMT wird bei einer Vererbung immer angelegt

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
fauler Sack schrieb:
> VMT wird bei einer Vererbung immer angelegt

Keineswegs, nur wenn virtual Funktionen existieren. Ein Grundsatz von 
c++ ist ja: you don't pay for what you don't use.

Autor: fauler Sack (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Ja, ja, bin wohl noch nicht richtig wach.

Autor: Torsten Robitzki (Firma: robitzki.de) (torstenrobitzki)
Datum:

Bewertung
-3 lesenswert
nicht lesenswert
Maier schrieb:

> Ich frage mich gerade ob es irgendwelche Nachteile hätte allgemein vor
> jeden Destruktor ein Virtual zu setzen?

Zu den genannten Nachteilen kommt noch hinzu, dass man mit einem 
virtuellen destructor üblicherweise auch dokumentiert, dass die Klasse 
als Basisklasse gedacht ist.

Im modernen C++ braucht man ja auch nur noch relativ selten nicht 
virtuelle Destruktoren.

Autor: Noch einer (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
>Ein Grundsatz von
>c++ ist ja: you don't pay for what you don't use.

Dieser Grundsatz liesse sich auch anders realisieren. Wenn Compiler und 
Linker zusammenarbeiten, können die selbst entscheiden, ob eine VMT 
erforderlich ist.

Im Vergleich zu profile guided optimizations oder LLVM-IR dürfte so 
etwas trivial sein.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Noch einer schrieb:
> Wenn Compiler und Linker zusammenarbeiten, können die selbst
> entscheiden, ob eine VMT erforderlich ist.

Woher weiß denn der Linker, ob eine (ggf. dynamisch) nachgeladene 
Bibliothek Funktionen überschreibt? Wenn eine Bibliothek sich ändert und 
Funktionen hinzufügt, müssten der dynamische Loader den Code der 
Anwendung ändern und komplett neu linken. Das ist m.W. mit keinem 
existierenden Tool möglich (ohne richtiges Neu Kompilieren mithilfe des 
Codes).

Und will der Programmierer überhaupt, dass die Funktion immer 
automatisch in der am meisten abgeleiteten Klasse aufgerufen wird?
In Java bspw sieht das anders aus: Da ist dank Bytecode Problem 1 lösbar 
und Punkt 2 ist halt einfach festgeschrieben. Tatsächlich kann zB die 
Hotspot VM virtuelle Funktionen in manchen Fällen wegoptimieren, was 
Java dort effizienter als C und C++ macht.

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Noch einer schrieb:
>>Ein Grundsatz von
>>c++ ist ja: you don't pay for what you don't use.
>
> Dieser Grundsatz liesse sich auch anders realisieren. Wenn Compiler und
> Linker zusammenarbeiten, können die selbst entscheiden, ob eine VMT
> erforderlich ist.

Das wäre dann eine Optimierung, deren Nutzung das ABI ändert.

> Im Vergleich zu profile guided optimizations oder LLVM-IR dürfte so
> etwas trivial sein.

Ganz so einfach ist es nicht, wenn man z.B. an Plug-Ins denkt, also zur 
Laufzeit dynamisch nachgeladene Bibliotheken. Mit denen muss das auch 
funktionieren. Und woher soll der Compiler im Hauptprogramm wissen, ob 
das Plug-In später mal den virtuellen Destruktor nutzt?

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
... man ist leicht versucht, über solche Optimierungen von C++ 
nachzudenken, auch solche die die Binärkompatibilität verbessern würden. 
Tatsächlich würden solche Änderungen aus C++ praktisch Java machen. Da 
kann man auch einfach direkt Java nehmen.

Ein weiteres Problem: template-Code müsste ggf. neu generiert werden 
(z.B. std::vector kann sich anders verhalten je nachdem ob der 
übergebene Typ virtuelle Funktionen hat oder nicht); das dürfte in etwa 
so schön sein wie export templates, und die wurden als nicht umsetzbar 
aus dem Standard entfernt.

Autor: Noch einer (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
>zur Laufzeit dynamisch nachgeladene Bibliotheken

Das Problem hast du heute auch schon. Der Compiler muss die 
Deklarationen der Klassen in der shared library kennen. Schau dir mal 
an, was die Qt für Verrenkungen enthält, damit sie die privat member 
nachträglich ändern können.

Funktioniert eigentlich folgende Konstruktion?
-Die Basisklasse aus der shared library hat keine VMT.
-Du leitest eine Klasse ab, die virtuelle Methoden enthält.
-Du übergibst der shared library einen Pointer auf ein Objekt der 
abgeleiteten Klasse.

Autor: Torsten Robitzki (Firma: robitzki.de) (torstenrobitzki)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Noch einer schrieb:

> Funktioniert eigentlich folgende Konstruktion?
> -Die Basisklasse aus der shared library hat keine VMT.
> -Du leitest eine Klasse ab, die virtuelle Methoden enthält.
> -Du übergibst der shared library einen Pointer auf ein Objekt der
> abgeleiteten Klasse.

Ja, Objekte der Basisklasse haben keine v'table, Objekte der 
abgeleiteten Klasse haben eine v'table.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Noch einer schrieb:
> Das Problem hast du heute auch schon. Der Compiler muss die
> Deklarationen der Klassen in der shared library kennen.
Genau... Daher wird angenommen dass die sich nicht ändern.

Noch einer schrieb:
> Funktioniert eigentlich folgende Konstruktion?
Ich glaube schon. Bei(vor!) der Übergabe wird ja automatisch auf die 
Basis-Klasse gecastet. Natürlich werden dann keine virtuellen Funktionen 
in der abgeleiteten Klasse aufgerufen.

Autor: Oliver S. (oliverso)
Datum:

Bewertung
-3 lesenswert
nicht lesenswert
... und wenn die lib dann ein delete auf den Pointer ausführt, knallt 
es. Nur deshalb braucht es ja den virtuellen Destruktor.

Oliver

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Oliver S. schrieb:
> ... und wenn die lib dann ein delete auf den Pointer ausführt, knallt
> es.
Nur wenn der Destruktor der abgeleiteten Klasse überhaupt etwas tut. Das 
ist aber auch dann der Fall wenn (indirekt) eine Variable mit 
nichttrivialem Destruktor vorhanden ist, was sich nicht so leicht 
überblicken lässt... std::is_trivially_destructible kann da helfen.

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Noch einer schrieb:
>>zur Laufzeit dynamisch nachgeladene Bibliotheken
>
> Das Problem hast du heute auch schon. Der Compiler muss die
> Deklarationen der Klassen in der shared library kennen.

Er muss für den Aufruf der virtuellen Memberfunktionen nur die 
Deklaration der Basisklasse kennen. Das ist ja gerade der Witz dabei.

> Schau dir mal an, was die Qt für Verrenkungen enthält, damit sie die
> privat member nachträglich ändern können.

Ich weiß nicht, was du damit meinst. Wer soll wo private Member 
nachträglich ändern?

> Funktioniert eigentlich folgende Konstruktion?
> -Die Basisklasse aus der shared library hat keine VMT.
> -Du leitest eine Klasse ab, die virtuelle Methoden enthält.
> -Du übergibst der shared library einen Pointer auf ein Objekt der
> abgeleiteten Klasse.

Wüßte nicht, warum das nicht genau gleich funktionieren sollte, als 
wären alle Klassen in einem gemeinsamen Executable.

Autor: Noch einer (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
>Wer soll wo private Member nachträglich ändern?

Trolltech hat den Anspruch, ein altes Programm, das mit QT 5.0.0 
compiliert wurde, soll mit allen nachfolgenden Qt5 shared libraries 
funktionieren.

War nur ein Beispiel, c++ shared libraries sind nicht wirklich optimal 
gelöst.

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Noch einer schrieb:
>>Wer soll wo private Member nachträglich ändern?
>
> Trolltech hat den Anspruch, ein altes Programm, das mit QT 5.0.0
> compiliert wurde, soll mit allen nachfolgenden Qt5 shared libraries
> funktionieren.

Ach jetzt weiß ich, was du meinst. Das hat aber erstmal mit virtuellen 
Memberfunktionen nichts zu tun. Ja, man muss einige Dinge beachten, wenn 
man eine Bibliothek binärkompatibel zur Vorversion halten und trotzdem 
flexibel ändern können will.

> War nur ein Beispiel, c++ shared libraries sind nicht wirklich optimal
> gelöst.

Wie so oft gilt: Das Problem kann durch ein zusätzliches 
Indirektionslevel umgangen werden, wie es bei Qt gemacht wird. Kostet 
halt Performance. In Java ist das eben schon in die Sprache eingebaut.

Autor: Daniel R. (dan066)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Um mal zur ursprünglichen Frage zurückzukommen:
Ein virtueller Destruktor macht Sinn, wenn man eine Basisklasse hat und 
Pointer von deren Typ auf abgeleitete Klassen benutzen will (also 
Polymorphie). Zeigt ein Basisklassenpointer auf ein Objekt einer 
abgeleiteten Klasse und man löscht den Basisklassenpointer mit delete, 
wird der Destruktor der abgeleiteten Klasse nur aufgerufen wenn er 
virtuell ist.

Das heißt: Virtueller Destruktor ist sinnvoll, sobald es mind. eine 
virtuelle Funktion gibt. Mit dieser zeigt man, dass Polymorphie hier 
angedacht ist. Außerdem "bezahlt" man nicht mehr, da es dann bereits 
einen vTable gibt.

Den Destruktor immer virtuell zu machen führt nicht zu Fehlern, kostet 
ein paar Byte pro Objekt und man sagt damit etwas fragwürdiges aus wie: 
"Benutze meine Klasse nicht polymorph. Falls du es doch tust, habe ich 
dir schonmal eine potentielle Fehlerquelle ausgeschaltet."

Autor: Peter II (Gast)
Datum:

Bewertung
-2 lesenswert
nicht lesenswert
Daniel R. schrieb:
> Das heißt: Virtueller Destruktor ist sinnvoll, sobald es mind. eine
> virtuelle Funktion gibt. Mit dieser zeigt man, dass Polymorphie hier
> angedacht ist. Außerdem "bezahlt" man nicht mehr, da es dann bereits
> einen vTable gibt.

das hätte man doch aber gleich so in der Sprache verankern sollen. 
Sobald eine Funktion virtuelle ist, dann ist die Destruktor automatisch 
virtuell.

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Peter II schrieb:
> das hätte man doch aber gleich so in der Sprache verankern sollen.

Hätte man können, und wäre wahrscheinlich auch sinnvoll gewesen. Aber so 
ist das eben: Der eine beschwert sich, dass zu wenig automatisch 
passiert, der andere beschwert sich, dass es schon zu viel ist.
Im Prinzip braucht man den virtuellen Destruktor nicht zwingend, wenn 
man virtuelle Memberfunktionen hat, sondern nur, wenn man eine Instanz 
der abgeleiteten Klasse über einen Zeiger auf die Basisklasse zerstören 
will. Auf der anderen Seite wäre der Overhead vernachlässigbar (ein 
Zeiger mehr in der vtable, aber nur pro Klasse, nicht pro Instanz).

Autor: mh (Gast)
Datum:

Bewertung
-2 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Auf der anderen Seite wäre der Overhead vernachlässigbar (ein
> Zeiger mehr in der vtable, aber nur pro Klasse, nicht pro Instanz).

Und der Overhead beim Aufruf des Destruktors, inkl. aller nicht 
möglichen Optimierungen.

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
-2 lesenswert
nicht lesenswert
mh schrieb:
> Rolf M. schrieb:
>> Auf der anderen Seite wäre der Overhead vernachlässigbar (ein
>> Zeiger mehr in der vtable, aber nur pro Klasse, nicht pro Instanz).
>
> Und der Overhead beim Aufruf des Destruktors, inkl. aller nicht
> möglichen Optimierungen.

... der aber nur nötig ist, wenn auch ein virtueller Destruktor nötig 
ist. Wenn man das Objekt nicht über einen Basisklassen-Zeiger zerstört, 
warum sollte der Compiler dann den Umweg über die vtable gehen? Nur weil 
sie halt da ist?

Autor: Torsten Robitzki (Firma: robitzki.de) (torstenrobitzki)
Datum:

Bewertung
-2 lesenswert
nicht lesenswert
Peter II schrieb:
> Daniel R. schrieb:
>> Das heißt: Virtueller Destruktor ist sinnvoll, sobald es mind. eine
>> virtuelle Funktion gibt. Mit dieser zeigt man, dass Polymorphie hier
>> angedacht ist. Außerdem "bezahlt" man nicht mehr, da es dann bereits
>> einen vTable gibt.
>
> das hätte man doch aber gleich so in der Sprache verankern sollen.
> Sobald eine Funktion virtuelle ist, dann ist die Destruktor automatisch
> virtuell.

Du brauchst den virtuellen d'tor nur, wenn Du ein Objekt über einen 
Zeiger auf die Basisklasse löschen (deleten) möchtest. Es gibt aber auch 
Anwendungsfälle, wo dies nicht nötig ist. Dann kann man z.B. immer noch 
das Löschen der Basisklasse unterbinden (protected d'tor oder private 
operator delete).

Ja, C++ ist halte recht gewachsen, und an vielen Stellen würde man heute 
andere defaults wählen.

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> ... der aber nur nötig ist, wenn auch ein virtueller Destruktor nötig
> ist. Wenn man das Objekt nicht über einen Basisklassen-Zeiger zerstört,
> warum sollte der Compiler dann den Umweg über die vtable gehen? Nur weil
> sie halt da ist?

Der Compiler muss über die vtable gehen, wenn er nicht 100% sicher sein 
kann, dass statischer == dynamischer Typ ist. Und das zu erkennen ist 
für den Compiler nicht immer einfach.

Wenn der Destruktor nicht virtuell ist, muss statischer == dynamischer 
Typ sein, alles andere wäre undefined behaviour.

Vermutlich hätte man bei vorhandenen virtuellen Methoden den Destruktor 
automatisch virtuell machen können, ohne zusätzlichen Overhead, wenn es 
gleichzeitig den finally specifier gegeben hätte. Aber den gibt es erst 
seit c++11. Und dann müsste man immer an das finally denken, statt an 
das virtual und hat nichts gewonnen.

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
mh schrieb:
> Der Compiler muss über die vtable gehen, wenn er nicht 100% sicher sein
> kann, dass statischer == dynamischer Typ ist. Und das zu erkennen ist
> für den Compiler nicht immer einfach.

Hmm, stimmt. Die Klasse könnte ja selbst wieder eine Basisklasse sein.

Autor: Chris F. (chfreund) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Daniel R. schrieb:
> Um mal zur ursprünglichen Frage zurückzukommen:
> Ein virtueller Destruktor macht Sinn, wenn man eine Basisklasse hat und
> Pointer von deren Typ auf abgeleitete Klassen benutzen will (also
> Polymorphie). Zeigt ein Basisklassenpointer auf ein Objekt einer
> abgeleiteten Klasse und man löscht den Basisklassenpointer mit delete,
> wird der Destruktor der abgeleiteten Klasse nur aufgerufen wenn er
> virtuell ist.
>
> Das heißt: Virtueller Destruktor ist sinnvoll, sobald es mind. eine
> virtuelle Funktion gibt. Mit dieser zeigt man, dass Polymorphie hier
> angedacht ist. Außerdem "bezahlt" man nicht mehr, da es dann bereits
> einen vTable gibt.
>
> Den Destruktor immer virtuell zu machen führt nicht zu Fehlern, kostet
> ein paar Byte pro Objekt und man sagt damit etwas fragwürdiges aus wie:
> "Benutze meine Klasse nicht polymorph. Falls du es doch tust, habe ich
> dir schonmal eine potentielle Fehlerquelle ausgeschaltet."


Das ist nicht die einzige Form von Polymorphie in C++, es ist nur eine 
allgemeine Form von Polymorphie die bei C++ "Überschreibung 
nichtvirtueller Elementfunktionen" heisst, es gibt z.B. auch noch 
Überladung und virtuelle Vererbung. Der Rest von Daniels Ansatz ist 
natürlich richtig. Polymorphie bedeutet allgemein, dass sich ein 
Objekt/Vorgang je nach Kontext unterschiedlich verhält. Was der TO fragt 
wirkt sich auf alle Formen von Polymorphie aus, nicht nur auf die hier 
beschriebene. Außerdem gilt das gleiche Verhalten auch für einen 
nichtvirtuellen Destruktor in der Basisklasse wenn man die Ableitung 
zerstört. Das hat auf ganz viele weitere Fälle eine Auswirkung, da hat 
Daniel auch Scott Meyers in Effective C++ auf seiner Seite. Sobald eine 
andere virtuelle Methode (Elementfunktion) vorhanden ist sollte der 
Destruktor auch virtuell sein.

Als Erklärung für den TO: Sobald der Kontext nicht mehr die Klasse des 
nichtvirtuellen Destruktors ist wird dieser nicht mehr aufgerufen.

Die Antwort für die Frage des TO ist ganz einfach:

Wenn eine Klasse keine virtuellen Mitglieder hat bewirkt ein virtueller 
Konstruktor einen höheren Resourcenbedarf.

Autor: Wilhelm M. (wimalopaan)
Datum:

Bewertung
2 lesenswert
nicht lesenswert
Chris F. schrieb:

> Das ist nicht die einzige Form von Polymorphie in C++, es ist nur eine

Nur mal so am Rande: C++ ist eine Multiparadigmen-Sprache (das macht sie 
eben zu einem recht komplexen Werkzeugkasten) und es gibt

- Inklusionspolymorphie
- parametrische Polymorphie
- ad-hoc Polymorphie (aka Funktionsüberladung)
- Operator-Polymorphie (in C++ (fast) identisch mit Funktioneüberladung)

Wenn man sehr spitzfindig ist, könnte man die Liste ggf. noch weiter 
unterteilen.
Wobei m.E die Form der param. Polymorhie in C++ das mächtigste Werkzeug 
ist.

: Bearbeitet durch User
Autor: Paul Baumann (paul_baumann)
Datum:

Bewertung
-4 lesenswert
nicht lesenswert
>>Re: C++ virtual destructor

10 Mann, 30 Meinungen.

Bei so einem komplizierten Kram muß man sich nicht wundern, wenn der 
Programmschreiber zum REALEN DESTRUKTOR wird und den Rechner zum Fenster 
'naus haut.
:))
MfG Paul

Autor: Oliver S. (oliverso)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Paul B. schrieb:
> 10 Mann, 30 Meinungen.

Dann noch eine, siehe #4:

http://www.gotw.ca/publications/mill18.htm

Oliver

Autor: Carl Drexler (jcw2)
Datum:

Bewertung
-4 lesenswert
nicht lesenswert
Oliver S. schrieb:
> Paul B. schrieb:
>> 10 Mann, 30 Meinungen.
>
> Dann noch eine, siehe #4:
>
> http://www.gotw.ca/publications/mill18.htm
>
> Oliver

Wenn auch nicht die Allerneueste:

"This article appeared in C/C++ Users Journal, 19(9), September 2001."

Autor: Oliver S. (oliverso)
Datum:

Bewertung
-3 lesenswert
nicht lesenswert
Je nun. Manche Erkenntnisse veralten nicht.

Oliver

Autor: Nase (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Daniel R. schrieb:
> Das heißt: Virtueller Destruktor ist sinnvoll, sobald es mind. eine
> virtuelle Funktion gibt.
Das ist aber so nicht richtig oder zumindest nicht vollständig.

Man nehme eine leere Klasse. Und eine davon abgeleitete Klasse, deren 
Konstruktor einer abgeleiteten Klasse z.B. Speicher auf dem Heap 
anfordert und diesen in seinem Destruktor wieder freigibt.

Schon dann braucht man in der leeren Klasse einen virtuellen Destruktor. 
Sonst wird der Destruktor der abgeleiteten Klasse nicht aufgerufen, wenn 
man über einen Zeiger auf die (leere) Basisklasse operiert.

Autor: Hào Nguyễn Danh (ho_n)
Datum:

Bewertung
-3 lesenswert
nicht lesenswert
Ja, bei virtual musst du ihn in jedem Fall überschreiben.. Wenn auch nur 
eine einzige virtuelle Methode existiert, legt der
Compiler den ganzen Verwaltungsaufwand für virtuelle Methoden an.

Beitrag #5193672 wurde vom Autor gelöscht.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.