
Lodel
Ich habe ein solides Verständnis von den meisten OOP
Theorie, aber das einzige, was mich sehr verwirrt, sind virtuelle Destruktoren.
Ich dachte, dass der Destruktor immer aufgerufen wird, egal was passiert und für jedes Objekt in der Kette.
Wann soll man sie virtuell machen und warum?

Luca Touraille
Virtuelle Destruktoren sind nützlich, wenn Sie möglicherweise eine Instanz einer abgeleiteten Klasse über einen Zeiger auf die Basisklasse löschen könnten:
class Base
{
// some virtual methods
};
class Derived : public Base
{
~Derived()
{
// Do some important cleanup
}
};
Hier werden Sie feststellen, dass ich den Destruktor von Base nicht als deklariert habe virtual
. Schauen wir uns nun den folgenden Ausschnitt an:
Base *b = new Derived();
// use b
delete b; // Here's the problem!
Da der Destruktor von Base dies nicht ist virtual
und b
ist ein Base*
zeigt auf a Derived
Objekt, delete b
hat undefiniertes Verhalten:
[In delete b
]wenn sich der statische Typ des zu löschenden Objekts von seinem dynamischen Typ unterscheidet, muss der statische Typ eine Basisklasse des dynamischen Typs des zu löschenden Objekts sein und Der statische Typ muss einen virtuellen Destruktor haben oder das Verhalten ist undefiniert.
In den meisten Implementierungen wird der Aufruf des Destruktors wie jeder nicht virtuelle Code aufgelöst, was bedeutet, dass der Destruktor der Basisklasse aufgerufen wird, aber nicht der der abgeleiteten Klasse, was zu einem Ressourcenleck führt.
Zusammenfassend sollten Sie immer die Destruktoren der Basisklassen erstellen virtual
wenn sie polymorph manipuliert werden sollen.
Wenn Sie das Löschen einer Instanz durch einen Basisklassenzeiger verhindern möchten, können Sie den Destruktor der Basisklasse geschützt und nicht virtuell machen; Dadurch lässt der Compiler Sie nicht aufrufen delete
auf einem Basisklassenzeiger.
Mehr über Virtualität und den virtuellen Basisklassendestruktor erfahren Sie in diesen Artikel von Herb Sutter.

Tunvir Rahman Tusher
Ein virtueller Konstruktor ist nicht möglich, aber ein virtueller Destruktor ist möglich. Lass uns experimentieren…….
#include <iostream>
using namespace std;
class Base
{
public:
Base(){
cout << "Base Constructor Called\n";
}
~Base(){
cout << "Base Destructor called\n";
}
};
class Derived1: public Base
{
public:
Derived1(){
cout << "Derived constructor called\n";
}
~Derived1(){
cout << "Derived destructor called\n";
}
};
int main()
{
Base *b = new Derived1();
delete b;
}
Der obige Code gibt Folgendes aus:
Base Constructor Called
Derived constructor called
Base Destructor called
Die Konstruktion des abgeleiteten Objekts folgt der Konstruktionsregel, aber wenn wir den “b”-Zeiger (Basiszeiger) löschen, haben wir festgestellt, dass nur der Basisdestruktor aufgerufen wird. Aber das darf nicht passieren. Um das Richtige zu tun, müssen wir den Basisdestruktor virtuell machen. Nun sehen Sie, was im Folgenden passiert:
#include <iostream>
using namespace std;
class Base
{
public:
Base(){
cout << "Base Constructor Called\n";
}
virtual ~Base(){
cout << "Base Destructor called\n";
}
};
class Derived1: public Base
{
public:
Derived1(){
cout << "Derived constructor called\n";
}
~Derived1(){
cout << "Derived destructor called\n";
}
};
int main()
{
Base *b = new Derived1();
delete b;
}
Die Ausgabe hat sich wie folgt geändert:
Base Constructor Called
Derived Constructor called
Derived destructor called
Base destructor called
Die Zerstörung des Basiszeigers (der eine Zuweisung auf das abgeleitete Objekt übernimmt!) folgt also der Zerstörungsregel, dh zuerst das Derived, dann die Basis. Andererseits gibt es nichts Besseres als einen virtuellen Konstruktor.
Deklarieren Sie Destruktoren in polymorphen Basisklassen als virtuell. Dies ist Punkt 7 in Effektives C++ von Scott Meyers. Meyers fährt fort, das zusammenzufassen, wenn eine Klasse hat irgendein virtuelle Funktion, sie sollte einen virtuellen Destruktor haben, und dass Klassen, die nicht als Basisklassen entworfen wurden oder nicht dafür entworfen wurden, polymorph verwendet zu werden, sollten nicht deklarieren Sie virtuelle Destruktoren.

BigSandwich
Beachten Sie auch, dass das Löschen eines Basisklassenzeigers, wenn kein virtueller Destruktor vorhanden ist, dazu führt undefiniertes Verhalten. Was ich erst kürzlich gelernt habe:
Wie sollte sich das überschreibende Löschen in C++ verhalten?
Ich benutze C++ seit Jahren und schaffe es immer noch, mich zu erhängen.

jaraaj
Machen Sie den Destruktor virtuell, wenn Ihre Klasse polymorph ist.

Abyx
Destruktor über einen Zeiger auf eine Basisklasse aufrufen
struct Base {
virtual void f() {}
virtual ~Base() {}
};
struct Derived : Base {
void f() override {}
~Derived() override {}
};
Base* base = new Derived;
base->f(); // calls Derived::f
base->~Base(); // calls Derived::~Derived
Der virtuelle Destruktoraufruf unterscheidet sich nicht von anderen virtuellen Funktionsaufrufen.
Zum base->f()
wird der Anruf an weitergeleitet Derived::f()
und es ist das gleiche für base->~Base()
– seine übergeordnete Funktion – die Derived::~Derived()
wird angerufen werden.
Dasselbe passiert, wenn der Destruktor indirekt aufgerufen wird, z delete base;
. Die delete
Aussage wird anrufen base->~Base()
die versendet werden Derived::~Derived()
.
Abstrakte Klasse mit nicht virtuellem Destruktor
Wenn Sie ein Objekt nicht über einen Zeiger auf seine Basisklasse löschen möchten, ist kein virtueller Destruktor erforderlich. Mach es einfach protected
damit es nicht versehentlich aufgerufen wird:
// library.hpp
struct Base {
virtual void f() = 0;
protected:
~Base() = default;
};
void CallsF(Base& base);
// CallsF is not going to own "base" (i.e. call "delete &base;").
// It will only call Base::f() so it doesn't need to access Base::~Base.
//-------------------
// application.cpp
struct Derived : Base {
void f() override { ... }
};
int main() {
Derived derived;
CallsF(derived);
// No need for virtual destructor here as well.
}

Prakash GiBBs
Vereinfacht gesagt zerstört der virtuelle Destruktor die Ressourcen in der richtigen Reihenfolge, wenn Sie einen Basisklassenzeiger löschen, der auf ein abgeleitetes Klassenobjekt zeigt.
#include<iostream>
using namespace std;
class B{
public:
B(){
cout<<"B()\n";
}
virtual ~B(){
cout<<"~B()\n";
}
};
class D: public B{
public:
D(){
cout<<"D()\n";
}
~D(){
cout<<"~D()\n";
}
};
int main(){
B *b = new D();
delete b;
return 0;
}
OUTPUT:
B()
D()
~D()
~B()
==============
If you don't give ~B() as virtual. then output would be
B()
D()
~B()
where destruction of ~D() is not done which leads to leak
10033700cookie-checkWann werden virtuelle Destruktoren verwendet?yes
Siehe dazu: Virtueller Destruktor
– Naveen
20. Januar 2009 um 13:04 Uhr
Jeder Destruktor Nieder wird trotzdem angerufen.
virtual
stellt sicher, dass es oben statt in der Mitte beginnt.– Muhende Ente
29. Juni 2013 um 0:32 Uhr
verwandte Frage: Wann sollten Sie keine virtuellen Destruktoren verwenden?
– Eitan T
4. August 2013 um 16:39 Uhr
Ich bin auch verwirrt von @MooingDucks Antwort. Sollte es nicht sein hoch anstatt Niederwenn Sie den Begriff der Unterklasse (unter) und Oberklasse (oben) verwenden?
– Nibor
20. Juni 2019 um 9:31 Uhr
@Nibor: Ja, wenn Sie diesen Begriff verwenden. Ungefähr die Hälfte der Leute, mit denen ich spreche, betrachtet Superklassen als „oben“ und die andere Hälfte betrachtet Superklassen als „unten“, also sind beides widersprüchliche Standards, was alles verwirrend macht. Ich denke, Superklasse als “oben” ist etwas häufiger, aber das wurde mir nicht beigebracht 🙁
– Muhende Ente
20. Juni 2019 um 17:09 Uhr