Wann werden virtuelle Destruktoren verwendet?

Lesezeit: 10 Minuten

Wann werden virtuelle Destruktoren verwendet
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?

  • 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

1647299411 36 Wann werden virtuelle Destruktoren verwendet
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.

  • Dies würde erklären, warum ich mit einer Fabrik, die ich zuvor gebaut habe, massive Lecks hatte. Alles macht jetzt Sinn. Danke

    – Lödel

    20. Januar 2009 um 13:08 Uhr

  • Nun, dies ist ein schlechtes Beispiel, da es keine Datenelemente gibt. Was, wenn Base und Derived verfügen über alle automatische Speichervariablen? dh es gibt keinen “speziellen” oder zusätzlichen benutzerdefinierten Code, der im Destruktor ausgeführt werden kann. Ist es dann in Ordnung, überhaupt keine Destruktoren zu schreiben? Oder wird die abgeleitete Klasse still haben Sie ein Speicherleck?

    – bobobobo

    8. Juli 2012 um 18:27 Uhr

  • Warten Sie, es wird ein undefiniertes Verhalten sein

    – bobobobo

    8. Juli 2012 um 18:29 Uhr

  • Aus dem Artikel von Herb Sutter: „Richtlinie Nr. 4: Ein Basisklassendestruktor sollte entweder öffentlich und virtuell oder geschützt und nicht virtuell sein.“

    – Eisbecher

    9. Februar 2016 um 8:22 Uhr

  • Auch aus dem Artikel – “Wenn Sie ohne einen virtuellen Destruktor polymorph löschen, beschwören Sie das gefürchtete Gespenst des “undefinierten Verhaltens” herauf, ein Gespenst, dem ich persönlich lieber nicht einmal in einer mäßig gut beleuchteten Gasse begegnen würde, vielen Dank.” lol

    – Bondolin

    29. Februar 2016 um 14:30 Uhr

Wann werden virtuelle Destruktoren verwendet
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.

  • “virtueller Konstruktor ist nicht möglich” bedeutet, dass Sie den virtuellen Konstruktor nicht selbst schreiben müssen. Die Konstruktion des abgeleiteten Objekts muss der Konstruktionskette von der abgeleiteten zur Basis folgen. Sie müssen also nicht das Schlüsselwort virtual für Ihren Konstruktor schreiben. Danke

    – Tunvir Rahman Tusher

    19. April 2013 um 6:50 Uhr

  • @Murkantilism, “virtuelle Konstruktoren können nicht ausgeführt werden” ist in der Tat wahr. Ein Konstruktor kann nicht als virtuell markiert werden.

    – cmeub

    21. April 2013 um 20:09 Uhr

  • @cmeub, aber es gibt eine Redewendung, um das zu erreichen, was Sie von einem virtuellen Konstruktor erwarten würden. Sehen parashift.com/c++-faq-lite/virtual-ctors.html

    – Umhang1232

    3. Oktober 2013 um 12:58 Uhr

  • @TunvirRahmanTusher könntest du bitte erklären, warum der Base Destructor heißt??

    – Rimalroshan

    11. November 2017 um 8:49 Uhr

  • @rimiro Es ist automatisch von c ++. Sie können dem Link folgen stackoverflow.com/questions/677620/…

    – Tunvir Rahman Tusher

    11. November 2017 um 12:01 Uhr

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.

  • +”Wenn eine Klasse eine virtuelle Funktion hat, sollte sie einen virtuellen Destruktor haben, und dass Klassen, die nicht als Basisklassen oder nicht zur polymorphen Verwendung entworfen wurden, keine virtuellen Destruktoren deklarieren sollten.”: Gibt es Fälle, in denen dies sinnvoll ist diese Regel brechen? Wenn nicht, wäre es sinnvoll, den Compiler diese Bedingung überprüfen zu lassen und einen Fehler auszugeben, wenn sie nicht erfüllt ist?

    – Giorgio

    6. Mai 2012 um 9:29 Uhr

  • @Giorgio Ich kenne keine Ausnahmen von der Regel. Aber ich würde mich nicht als C++-Experten einstufen, daher möchten Sie dies vielleicht als separate Frage posten. Eine Compiler-Warnung (oder eine Warnung von einem statischen Analysetool) macht für mich Sinn.

    – Bill die Eidechse

    6. Mai 2012 um 13:08 Uhr

  • Klassen können so gestaltet werden, dass sie nicht durch einen Zeiger eines bestimmten Typs gelöscht werden, aber dennoch virtuelle Funktionen haben – ein typisches Beispiel ist eine Callback-Schnittstelle. Man löscht seine Implementierung nicht durch einen Callback-Schnittstellenzeiger, da dieser nur zum Abonnieren dient, aber er hat virtuelle Funktionen.

    – dascandy

    15. Januar 2016 um 5:05 Uhr

  • @dascandy Genau – das oder alles viele andere Situationen, in denen wir polymorphes Verhalten verwenden, aber keine Speicherverwaltung über Zeiger durchführen – z. B. das Verwalten von Objekten mit automatischer oder statischer Dauer, wobei Zeiger nur als Beobachtungsrouten verwendet werden. In solchen Fällen ist es nicht erforderlich, einen virtuellen Destruktor zu implementieren. Da wir hier nur Personen zitieren, bevorzuge ich Sutter von oben: „Richtlinie Nr. 4: Ein Destruktor der Basisklasse sollte entweder öffentlich und virtuell oder geschützt und nicht virtuell sein.“ Letzteres stellt sicher, dass jedem, der versehentlich versucht, über einen Basiszeiger zu löschen, der Fehler seiner Wege angezeigt wird

    – Unterstrich_d

    23. April 2016 um 15:58 Uhr


  • @Giorgio Es gibt tatsächlich einen Trick, mit dem man einen virtuellen Aufruf eines Destruktors vermeiden kann: Binden Sie ein abgeleitetes Objekt über eine const-Referenz an eine Basis, wie z const Base& = make_Derived();. In diesem Fall ist der Destruktor der Derived prvalue wird aufgerufen, auch wenn es nicht virtuell ist, also spart man sich den durch vtables/vpointers eingeführten Overhead. Der Umfang ist natürlich sehr begrenzt. Andrei Alexandrescu hat dies in seinem Buch erwähnt Modernes C++-Design.

    – vssoftco

    2. November 2016 um 20:06 Uhr


1647299412 961 Wann werden virtuelle Destruktoren verwendet
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.

1647299412 889 Wann werden virtuelle Destruktoren verwendet
jaraaj

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

1647299412 701 Wann werden virtuelle Destruktoren verwendet
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.
}

1647299413 83 Wann werden virtuelle Destruktoren verwendet
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

  • Nicht über den virtuellen Basisdestruktor und den Aufruf verfügen delete auf einem Basiszeiger führt zu undefiniertem Verhalten.

    – James Adkison

    13. Oktober 2016 um 20:00 Uhr

  • @JamesAdkison warum führt es zu undefiniertem Verhalten??

    – Rimalroshan

    11. November 2017 um 11:06 Uhr


  • @rimiro Das sagt der Standard. Ich habe keine Kopie, aber der Link führt Sie zu einem Kommentar, in dem jemand auf die Stelle innerhalb des Standards verweist.

    – James Adkison

    11. November 2017 um 13:52 Uhr


  • @rimiro “Wenn das Löschen daher polymorph über die Basisklassenschnittstelle durchgeführt werden kann, muss es sich virtuell verhalten und virtuell sein. Tatsächlich erfordert die Sprache dies – wenn Sie ohne virtuellen Destruktor polymorph löschen, beschwören Sie das gefürchtete Gespenst herauf von “undefiniertes Verhalten”, ein Gespenst, dem ich persönlich nicht einmal in einer mäßig gut beleuchteten Gasse begegnen möchte, vielen Dank.” (gotw.ca/publications/mill18.htm) — Herb Sutter

    – James Adkison

    11. November 2017 um 13:54 Uhr

1003370cookie-checkWann werden virtuelle Destruktoren verwendet?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy