109 Stimmen

Wann sollten Sie keine virtuellen Destruktoren verwenden?

Gibt es jemals einen guten Grund für pas einen virtuellen Destruktor für eine Klasse deklarieren? Wann sollten Sie es ausdrücklich vermeiden, einen zu schreiben?

5voto

Steve Jessop Punkte 264569

Nicht alle C++-Klassen sind für die Verwendung als Basisklasse mit dynamischer Polymorphie geeignet.

Wenn Ihre Klasse für dynamische Polymorphie geeignet sein soll, muss ihr Destruktor virtuell sein. Darüber hinaus müssen alle Methoden, die eine Unterklasse möglicherweise überschreiben möchte (d. h. alle öffentlichen Methoden sowie möglicherweise einige intern verwendete geschützte Methoden), virtuell sein.

Wenn Ihre Klasse nicht für dynamische Polymorphie geeignet ist, sollte der Destruktor nicht als virtuell gekennzeichnet werden, da dies irreführend ist. Es ermutigt die Leute nur, Ihre Klasse falsch zu verwenden.

Hier ein Beispiel für eine Klasse, die sich nicht für dynamische Polymorphie eignet, selbst wenn ihr Destruktor virtuell wäre:

class MutexLock {
    mutex *mtx_;
public:
    explicit MutexLock(mutex *mtx) : mtx_(mtx) { mtx_->lock(); }
    ~MutexLock() { mtx_->unlock(); }
private:
    MutexLock(const MutexLock &rhs);
    MutexLock &operator=(const MutexLock &rhs);
};

Der ganze Sinn dieses Kurses ist es, sich auf den Stapel für RAII zu setzen. Wenn Sie Zeiger auf Objekte dieser Klasse weitergeben, ganz zu schweigen von deren Unterklassen, dann machen Sie etwas falsch.

3 Stimmen

Polymorphe Verwendung bedeutet nicht polymorphe Löschung. Es gibt viele Anwendungsfälle, in denen eine Klasse zwar virtuelle Methoden, aber keinen virtuellen Destruktor hat. Betrachten Sie ein typisches statisch definiertes Dialogfeld in so ziemlich jedem GUI-Toolkit. Das Elternfenster wird die Kindobjekte zerstören, und es kennt den genauen Typ eines jeden, aber alle Kindfenster werden auch polymorph in einer beliebigen Anzahl von Orten verwendet, wie z.B. Treffertests, Zeichnen, Zugänglichkeits-APIs, die den Text für Text-to-Speech-Engines abrufen, usw.

4 Stimmen

Stimmt, aber der Fragesteller möchte wissen, wann man einen virtuellen Destruktor gezielt vermeiden sollte. Für das von Ihnen beschriebene Dialogfeld ist ein virtueller Destruktor zwar sinnlos, aber IMO nicht schädlich. Ich bin mir nicht sicher, ob ich jemals ein Dialogfeld mit einem Basisklassenzeiger löschen muss - zum Beispiel könnte ich in Zukunft wollen, dass mein übergeordnetes Fenster seine Kindobjekte mit Hilfe von Fabriken erzeugt. Es ist also nicht eine Frage von zu vermeiden. virtuellen Destruktor, sondern nur, dass man sich nicht die Mühe machen sollte, einen zu haben. Ein virtueller Destruktor für eine Klasse, die nicht für eine Ableitung geeignet ist ist schädlich, weil es irreführend ist.

4voto

kidfisto Punkte 41

Ein guter Grund, einen Destruktor nicht als virtuell zu deklarieren, besteht darin, dass dies Ihre Klasse davor bewahrt, eine virtuelle Funktionstabelle hinzuzufügen, und das sollten Sie nach Möglichkeit vermeiden.

Ich weiß, dass viele Leute es vorziehen, Destruktoren immer als virtuell zu deklarieren, nur um auf der sicheren Seite zu sein. Aber wenn Ihre Klasse keine anderen virtuellen Funktionen hat, dann hat ein virtueller Destruktor wirklich keinen Sinn. Selbst wenn Sie Ihre Klasse an andere Personen weitergeben, die dann andere Klassen davon ableiten, hätten diese keinen Grund, jemals delete für einen Zeiger aufzurufen, der zu Ihrer Klasse upgecastet wurde - und wenn sie es doch tun, würde ich dies als Fehler betrachten.

Okay, es gibt eine einzige Ausnahme, nämlich wenn Ihre Klasse (miss-)gebraucht wird, um abgeleitete Objekte polymorph zu löschen, aber dann wissen Sie - oder die anderen - hoffentlich, dass dies einen virtuellen Destruktor erfordert.

Anders ausgedrückt, wenn Ihre Klasse einen nicht-virtuellen Destruktor hat, ist dies eine sehr klare Aussage: "Benutzt mich nicht zum Löschen von abgeleiteten Objekten!"

3voto

Mark Ransom Punkte 283960

Wenn Sie eine sehr kleine Klasse mit einer großen Anzahl von Instanzen haben, kann der Overhead eines vtable-Zeigers einen Unterschied in der Speichernutzung Ihres Programms ausmachen. Solange Ihre Klasse keine anderen virtuellen Methoden hat, können Sie diesen Overhead einsparen, indem Sie den Destruktor nicht-virtuell machen.

1voto

Jørn Jensen Punkte 948

Normalerweise deklariere ich den Destruktor als virtuell, aber wenn Sie leistungsrelevanten Code haben, der in einer inneren Schleife verwendet wird, möchten Sie vielleicht die Suche nach der virtuellen Tabelle vermeiden. Das kann in manchen Fällen wichtig sein, z. B. bei der Kollisionsprüfung. Aber seien Sie vorsichtig, wie Sie diese Objekte zerstören, wenn Sie Vererbung verwenden, oder Sie werden nur die Hälfte des Objekts zerstören.

Beachten Sie, dass die virtuelle Tabelle für ein Objekt nachgeschlagen wird, wenn cualquier Methode auf diesem Objekt ist virtuell. Es macht also keinen Sinn, die virtuelle Spezifikation für einen Destruktor zu entfernen, wenn Sie andere virtuelle Methoden in der Klasse haben.

1voto

Lisa Punkte 439

Wenn Sie unbedingt sicherstellen müssen, dass Ihre Klasse keine vtable hat, dann dürfen Sie auch keinen virtuellen Destruktor haben.

Dies ist ein seltener Fall, aber es kommt vor.

Das bekannteste Beispiel für ein Muster, das dies tut, sind die DirectX-Klassen D3DVECTOR und D3DMATRIX. Dabei handelt es sich um Klassenmethoden anstelle von Funktionen für den syntaktischen Zucker, aber die Klassen haben absichtlich keine vtable, um den Funktionsoverhead zu vermeiden, da diese Klassen speziell in der inneren Schleife vieler Hochleistungsanwendungen verwendet werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X