1935 Stimmen

Wann sollten virtuelle Destruktoren verwendet werden?

Ich habe ein solides Verständnis der meisten OOP Theorie, aber die eine Sache, die mich sehr verwirrt, sind virtuelle Zerstörer.

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

Wann sollen Sie sie virtuell machen und warum?

18voto

Abyx Punkte 11461

Aufruf des Destruktors über einen Zeiger auf eine Basisklasse

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 eines virtuellen Destruktors unterscheidet sich nicht von jedem anderen Aufruf einer virtuellen Funktion.

Für base->f() wird der Anruf weitergeleitet an Derived::f() und das Gleiche gilt für base->~Base() - seine übergeordnete Funktion - die Derived::~Derived() aufgerufen werden.

Dasselbe geschieht, wenn der Destruktor indirekt aufgerufen wird, z. B. delete base; . Die delete Die Anweisung ruft base->~Base() die versandt werden an Derived::~Derived() .

Abstrakte Klasse mit nicht-virtuellem Destruktor

Wenn Sie ein Objekt nicht über einen Zeiger auf seine Basisklasse löschen wollen, besteht keine Notwendigkeit für einen virtuellen Destruktor. Machen Sie es einfach protected damit sie 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.
}

16voto

Mukul Kashmira Punkte 179

Das virtuelle Schlüsselwort für den Destruktor ist notwendig, wenn Sie möchten, dass die verschiedenen Destruktoren die richtige Reihenfolge einhalten, während die Objekte durch den Zeiger der Basisklasse gelöscht werden. zum Beispiel:

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

Wenn der Destruktor Ihrer Basisklasse virtuell ist, werden die Objekte in einer bestimmten Reihenfolge zerstört (zuerst das abgeleitete Objekt, dann die Basis). Wenn der Destruktor Ihrer Basisklasse NICHT virtuell ist, dann wird nur das Objekt der Basisklasse gelöscht (weil der Zeiger der Basisklasse "Base *myObj" ist). Es gibt also ein Speicherleck für abgeleitete Objekte.

13voto

Prakash GiBBs Punkte 267

Einfach zu sein, Der virtuelle Destruktor dient dazu, die Ressourcen in der richtigen Reihenfolge zu zerstören, wenn Sie einen Zeiger der Basisklasse löschen, der auf ein Objekt der abgeleiteten Klasse 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

10voto

Dragan Ostojic Punkte 99

Ich denke gerne über Schnittstellen und Implementierungen von Schnittstellen nach. In C++ ist die Schnittstelle eine rein virtuelle Klasse. Der Destruktor ist Teil der Schnittstelle und muss implementiert werden. Daher sollte der Destruktor rein virtuell sein. Wie sieht es mit dem Konstruktor aus? Der Konstruktor ist eigentlich nicht Teil der Schnittstelle, da das Objekt immer explizit instanziiert wird.

6voto

betteroutthanin Punkte 6508

Wenn Sie shared_ptr (nur shared_ptr, nicht unique_ptr), müssen Sie den Destruktor der Basisklasse nicht virtuell haben:

#include <iostream>
#include <memory>

using namespace std;

class Base
{
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    ~Base(){ // not virtual
        cout << "Base Destructor called\n";
    }
};

class Derived: public Base
{
public:
    Derived(){
        cout << "Derived constructor called\n";
    }
    ~Derived(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    shared_ptr<Base> b(new Derived());
}

Ausgabe:

Base Constructor Called
Derived constructor called
Derived destructor called
Base Destructor called

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