Frage Wann sollten virtuelle Destruktoren verwendet werden?


Ich habe ein solides Verständnis der meisten OO-Theorie, aber die eine Sache, die mich sehr verwirrt, ist virtuelle Destruktoren.

Ich dachte, dass der Destruktor immer aufgerufen wird, egal was und für jedes Objekt in der Kette.

Wann soll man sie virtuell machen und warum?


1206
2018-01-20 12:58


Ursprung


Antworten:


Virtuelle Destruktoren sind nützlich, wenn Sie eine Instanz einer abgeleiteten Klasse über einen Zeiger auf die Basisklasse löschen können:

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 deklariert habe virtual. Sehen wir uns nun das folgende Snippet an:

Base *b = new Derived();
// use b
delete b; // Here's the problem!

Da Base Destruktor nicht ist virtual und b ist ein Base* zeigt auf a Derived Objekt, delete b hat undefiniertes Verhalten:

[Im delete b], wenn der statische Typ des   Das zu löschende Objekt unterscheidet sich von seinem dynamischen Typ, dem statischen   type soll eine Basisklasse des dynamischen Typs des Objekts sein   gelöscht und der statische Typ soll einen virtuellen Destruktor oder die   Verhalten ist nicht definiert.

In den meisten Implementierungen wird der Aufruf des Destruktors wie jeder nichtvirtuelle 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, machen Sie immer die Destruktoren der Basisklassen virtual wenn sie polymorph manipuliert werden sollen.

Wenn Sie das Löschen einer Instanz durch einen Basisklassenzeiger verhindern möchten, können Sie den Basisklassendestruktor als geschützt und nicht virtuell festlegen. Auf diese Weise lässt der Compiler Sie nicht aufrufen delete auf einem Basisklassenzeiger.

Sie können mehr über Virtualität und virtuellen Basisklassen-Destruktor in lernen Dieser Artikel von Herb Sutter.


1310
2018-01-20 13:04



Deklarieren Destruktoren virtuell in polymorphen Basisklassen. Dies ist Artikel 7 in Scott Meyers Wirksames C ++. Meyers fährt fort, dass wenn eine Klasse hat irgendein virtuelle Funktion, sollte es einen virtuellen Destruktor haben, und dass Klassen nicht als Basisklassen entworfen werden oder nicht entworfen werden, um polymorph verwendet zu werden nicht deklarieren Sie virtuelle Destruktoren.


165
2018-01-20 13:11



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. Dies muss jedoch nicht geschehen. Um das Richtige zu tun, müssen wir den Basisdestruktor virtuell machen. Jetzt lass sehen, 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

So folgt die Zerstörung des Basiszeigers (der eine Zuweisung auf das abgeleitete Objekt nimmt!) Der Zerstörungsregel, d. H. Zuerst die abgeleitete und dann die Basis. Auf der anderen Seite gibt es für den Konstruktor nichts wie einen virtuellen Konstruktor.


153
2018-04-09 13:39



Beachten Sie außerdem, dass das Löschen eines Basisklassenzeigers, wenn kein virtueller Destruktor vorhanden ist, zur Folge hat undefiniertes Verhalten. Etwas, das ich erst kürzlich gelernt habe:

Wie sollte sich überschreiben in C ++ löschen?

Ich benutze C ++ seit Jahren und es gelingt mir immer noch, mich selbst aufzuhängen.


37
2018-01-21 01:09



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


30
2018-01-20 13:01



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 Aufruf des virtuellen Destruktors unterscheidet sich nicht von anderen virtuellen Funktionsaufrufen.

Zum base->f(), der Anruf wird an gesendet Derived::f()und es ist dasselbe base->~Base() - seine vorrangige Funktion - die Derived::~Derived() wird angerufen werden.

Dasselbe passiert, wenn der Destruktor indirekt aufgerufen wird, z. delete base;. Das delete Anweisung wird aufrufen base->~Base() welche versendet werden soll Derived::~Derived().

Abstrakte Klasse mit nicht virtuellem Destruktor

Wenn Sie ein Objekt nicht über einen Zeiger auf seine Basisklasse löschen, 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.
}

10
2018-05-18 13:38



Ich denke gerne über Schnittstellen und Implementierungen von Schnittstellen nach. In C ++ ist die Sprachschnittstelle eine reine virtuelle Klasse. Destructor ist Teil der Schnittstelle und wird voraussichtlich implementiert. Daher sollte Destruktor rein virtuell sein. Wie wäre es mit Konstruktor? Konstruktor ist eigentlich nicht Teil der Schnittstelle, da Objekt immer explizit instanziiert wird.


7
2017-11-08 16:28



Um einfach zu sein, Der virtuelle Destruktor soll die Ressourcen in der richtigen Reihenfolge zerstören, wenn Sie einen Basisklassenzeiger löschen, der auf das abgeleitete Klassenobjekt verweist.

 #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


6
2017-08-26 05:33



Ein virtuelles Schlüsselwort für den Destruktor ist erforderlich, wenn verschiedene Destruktoren der richtigen Reihenfolge folgen sollen, während Objekte durch den Basisklassenzeiger gelöscht werden. beispielsweise:

Base *myObj = new Derived();
// Some code which is using myObj object
myObj->fun();
//Now delete the object
delete myObj ; 

Wenn der abgeleitete Klassendestruktor virtuell ist, werden Objekte in einer Reihenfolge (zuerst abgeleitetes Objekt, dann Basis) gelöscht. Wenn der abgeleitete Klassendestruktor NICHT virtuell ist, wird nur das Basisklassenobjekt gelöscht (weil der Zeiger der Basisklasse "Base * myObj" entspricht). Es wird also ein Speicherleck für das abgeleitete Objekt geben.


5
2018-01-29 07:11