Werden virtuelle Destruktoren vererbt?

77

Wenn ich eine Basisklasse mit einem virtuellen Destruktor habe. Hat eine abgeleitete Klasse auch einen virtuellen Destruktor zu deklarieren?

class base {
public:
    virtual ~base () {}
};

class derived : base {
public:
    virtual ~derived () {} // 1)
    ~derived () {}  // 2)
};

Konkrete Fragen:

  1. Ist 1) und 2) gleich? Ist 2) aufgrund seiner Basis automatisch virtuell oder "stoppt" es die Virtualität?
  2. Kann der abgeleitete Destruktor weggelassen werden, wenn er nichts zu tun hat?
  3. Was ist die beste Vorgehensweise zum Deklarieren des abgeleiteten Destruktors? Es als virtuell, nicht virtuell deklarieren oder wenn möglich weglassen?
Kairol
quelle

Antworten:

92
  1. Ja, sie sind gleich. Die abgeleitete Klasse, die etwas nicht als virtuell deklariert, hindert es nicht daran, virtuell zu sein. Tatsächlich gibt es keine Möglichkeit, zu verhindern, dass eine Methode (einschließlich Destruktor) in einer abgeleiteten Klasse virtuell ist, wenn sie in einer Basisklasse virtuell war. In> = C ++ 11 können Sie finalverhindern, dass es in abgeleiteten Klassen überschrieben wird, aber das verhindert nicht, dass es virtuell ist.
  2. Ja, ein Destruktor in einer abgeleiteten Klasse kann weggelassen werden, wenn er nichts zu tun hat. Und es spielt keine Rolle, ob es virtuell ist oder nicht.
  3. Ich würde es wenn möglich weglassen. Und ich verwende das virtualSchlüsselwort aus Gründen der Klarheit immer wieder für virtuelle Funktionen in abgeleiteten Klassen. Die Benutzer sollten nicht die gesamte Vererbungshierarchie nach oben gehen müssen, um herauszufinden, dass eine Funktion virtuell ist. Wenn Ihre Klasse kopierbar oder verschiebbar ist, ohne dass Sie Ihre eigenen Kopier- oder Verschiebungskonstruktoren deklarieren müssen, werden Sie durch Deklarieren eines Destruktors jeglicher Art (auch wenn Sie ihn als definieren default) gezwungen, die Kopier- und Verschiebungskonstruktoren und Zuweisungsoperatoren zu deklarieren, wenn Sie möchten Sie als Compiler werden sie nicht mehr für Sie eingeben.

Als kleiner Punkt für Punkt 3. In Kommentaren wurde darauf hingewiesen, dass der Compiler, wenn ein Destruktor nicht deklariert ist, einen Standard generiert (der immer noch virtuell ist). Und diese Standardeinstellung ist eine Inline-Funktion.

Inline-Funktionen setzen möglicherweise mehr Ihres Programms Änderungen in anderen Teilen Ihres Programms aus und machen die Binärkompatibilität für gemeinsam genutzte Bibliotheken schwierig. Außerdem kann die erhöhte Kopplung angesichts bestimmter Arten von Änderungen zu einer starken Neukompilierung führen. Wenn Sie beispielsweise entscheiden, dass Sie wirklich eine Implementierung für Ihren virtuellen Destruktor wünschen, muss jeder Code, der ihn aufgerufen hat, neu kompiliert werden. Wenn Sie es im Klassenkörper deklariert und dann in einer .cppDatei als leer definiert hätten, könnten Sie es problemlos ändern, ohne es neu zu kompilieren.

Meine persönliche Entscheidung wäre immer noch, es wegzulassen, wenn es möglich ist. Meiner Meinung nach ist der Code unübersichtlich, und der Compiler kann manchmal mit einer Standardimplementierung etwas effizienter arbeiten als mit einer leeren. Aber es gibt Einschränkungen, unter denen Sie möglicherweise stehen, die dies zu einer schlechten Wahl machen.

Allgegenwärtig
quelle
1
Ich bin mit dem Teil "Auslassen" nicht einverstanden. Es kostet nicht viel, es im Header zu deklarieren und in der Quelle zu definieren (leerer Körper). Wenn Sie dies tun, können Sie jederzeit zurückkehren und einige Schritte hinzufügen (Protokollierung?), Ohne Ihre Clients zum erneuten Kompilieren zu zwingen.
Matthieu M.
1
Eigentlich deklariere ich nicht viel Funktion inline, nicht einmal die klassischen "Accessoren", aber wenn wir dann in einem großen Unternehmen arbeiten, haben wir möglicherweise höhere binäre Kompatibilitätsbeschränkungen als die meisten anderen.
Matthieu M.
3
Ich habe gerade aus diesem Vortrag gelernt , dass das Deklarieren des virtuellen Destruktors tatsächlich dazu führt, dass Ihre Klasse unbeweglich wird! Wenn Sie also einen virtuellen Destruktor deklarieren, müssen Sie auch die gesamte Regel 5 angeben, wenn Sie diese Eigenschaften wünschen. Noch mehr Grund, wenn möglich wegzulassen.
Neil Traft
1
"Wenn Ihre Klasse kopierbar oder verschiebbar ist, ohne dass Sie Ihre eigenen Kopier- oder Verschiebungskonstruktoren deklarieren müssen, werden Sie durch Deklarieren eines Destruktors jeglicher Art (auch wenn Sie ihn als Standard definieren) gezwungen, die Kopier- und Verschiebungskonstruktoren und Zuweisungsoperatoren zu deklarieren, wenn Sie möchten sie, da der Compiler sie nicht mehr für Sie einfügt. " Das ist falsch! en.cppreference.com/w/cpp/language/copy_constructor
Kaiserludi
1
@ Kaiserludi - Ich werde überprüfen, ob dies wahr ist und meine Antwort korrigieren.
Omnifarious
2
  1. Der Destruktor ist wie bei allen Methoden automatisch virtuell. Sie können nicht verhindern, dass eine Methode in C ++ virtuell ist (wenn sie bereits als virtuell deklariert wurde, dh in Java gibt es kein Äquivalent zu 'final').
  2. Ja, es kann weggelassen werden.
  3. Ich würde einen virtuellen Destruktor deklarieren, wenn ich beabsichtige, dass diese Klasse in eine Unterklasse unterteilt wird, unabhängig davon, ob es sich um eine andere Klasse handelt oder nicht. Ich ziehe es auch vor, Methoden weiterhin virtuell zu deklarieren, obwohl sie nicht benötigt werden. Dadurch bleiben die Unterklassen funktionsfähig, falls Sie sich jemals dazu entschließen sollten, die Vererbung zu entfernen. Aber ich nehme an, das ist nur eine Frage des Stils.
falstro
quelle
Destruktoren sind nicht automatisch virtuell und auch keine anderen Mitgliedsfunktionen.
1
@Neil; Natürlich nicht, ich habe mich im Beispiel auf den Destruktor bezogen (dh wo die Basisklasse eine virtuelle hat), nicht auf Destruktoren im Allgemeinen. Dies gilt für alle Methoden, nicht nur für Destruktoren.
Falstro
1
Seit C ++ 11 haben wir final.
whoan
1

Eine virtuelle Mitgliedsfunktion macht implizit jede Überladung dieser Funktion virtuell.

Das virtuelle in 1) ist also "optional", der virtuelle Basisklassendestruktor macht alle untergeordneten Destruktoren auch virtuell.

Klaim
quelle
0

1 / Ja 2 / Ja, es wird vom Compiler generiert. 3 / Die Wahl, ob es als virtuell deklariert werden soll oder nicht, sollte Ihrer Konvention für überschriebene virtuelle Mitglieder entsprechen. IMHO, es gibt gute Argumente in beide Richtungen. Wählen Sie einfach eines aus und folgen Sie ihm.

Ich würde es wenn möglich weglassen, aber eines kann Sie dazu veranlassen, es zu deklarieren: Wenn Sie das vom Compiler generierte verwenden, ist es implizit inline. Es gibt Zeiten, in denen Sie Inline-Mitglieder vermeiden möchten (z. B. dynamische Bibliotheken).

Ein Programmierer
quelle
0

Virtuelle Funktionen werden implizit überschrieben. Wenn die Methode einer untergeordneten Klasse mit der Methodensignatur der virtuellen Funktion einer Basisklasse übereinstimmt, wird sie überschrieben. Dies ist leicht zu verwirren und möglicherweise bricht während Refactoring, so gibt es overrideund finalSchlüsselwörter , da C ++ 11 explizit dieses Verhalten zu markieren. Es gibt entsprechende Warnungen, die das stille Verhalten beispielsweise -Wsuggest-overridein GCC verbieten .

Es gibt eine verwandte Frage für overrideund finalSchlüsselwörter zu SO: Ist das Schlüsselwort 'override' nur eine Überprüfung für eine überschriebene virtuelle Methode? .

Und die Dokumentation in der CPP-Referenz https://en.cppreference.com/w/cpp/language/override

Ob das overrideSchlüsselwort mit den Destruktoren verwendet werden soll, ist noch umstritten. Siehe zum Beispiel die Diskussion in dieser verwandten SO-Frage: Standardüberschreibung des virtuellen Destruktors Das Problem ist, dass sich die Semantik des virtuellen Destruktors von normalen Funktionen unterscheidet. Destruktoren sind verkettet, daher werden alle Basisklassen-Destruktoren nach Kind eins aufgerufen. Im Falle einer regulären Methodenbasis werden Implementierungen der überschriebenen Methode jedoch nicht standardmäßig aufgerufen. Sie können bei Bedarf manuell aufgerufen werden.

Alex Maystrenko
quelle