"pure virtual destructor"



  • was ist ein "pure virtual destructor"? ich bin mit virtuellen methoden vertraut, hab auch schon danach gegoogelt und jetzt zuletzt das gelesen:
    http://www.codersource.net/cpp_virtual_destructors.html

    dort steht unten als fußnote:

    Note:
    There is one more point to be noted regarding virtual destructor. We can't declare pure virtual destructor. Even if a virtual destructor is declared as pure, it will have to implement an empty body (at least) for the destructor.

    was heißt "pure"? entweder ist er virtuell, oder eben nicht, würde ich meinen. und warum muss ich (in einer abstrakten klasse) einen body für den Dtor definieren?



  • pure-virtual-Methoden muessen in abgeleiteten Klassen ebenfalls implementiert werden. Die Methode aus der Basisklasse hat dabei keinen Koerper


  • Mod

    zwutz schrieb:

    pure-virtual-Methoden muessen in abgeleiteten Klassen ebenfalls implementiert werden. Die Methode aus der Basisklasse hat dabei keinen Koerper

    Ja und nein. Eine rein virtuelle Funktion muss in einer abgeleiteten Klasse nicht rein-virtuell überschrieben werden, andernfalls ist auch die abgeleitete Klasse abstrakt. Unabhängig davon kann auch eine rein-virtuelle Funktion einen Funktionskörper haben, im Falle des Destruktors muss sie das sogar. Was man dabei bemerken kann, ist, dass sich im Falle des Destruktors die rein-virtuelle Eigenschaft (aus dieser Deklaration) auf die jeweilige Klasse beschränkt, weil jede abgeleitete Klasse in jedem Falle einen eigenen Destruktor deklariert. Ein vom Compiler implizit deklarierter Destruktor ist nie rein-virtuell.



  • camper schrieb:

    Ja und nein. Eine rein virtuelle Funktion muss in einer abgeleiteten Klasse nicht rein-virtuell ueberschrieben werden, andernfalls ist auch die abgeleitete Klasse abstrakt. Unabhaengig davon kann auch eine rein-virtuelle Funktion einen Funktionskoerper haben, im Falle des Destruktors muss sie das sogar. Was man dabei bemerken kann, ist, dass sich im Falle des Destruktors die rein-virtuelle Eigenschaft (aus dieser Deklaration) auf die jeweilige Klasse beschraenkt, weil jede abgeleitete Klasse in jedem Falle einen eigenen Destruktor deklariert. Ein vom Compiler implizit deklarierter Destruktor ist nie rein-virtuell.

    ah ok, danke.... wieder was dazugelernt...



  • und wieso muss der abstrakte destruktor einen funktionskörper haben? ich könnte mir denken, dass dieser für den fall, dass eine abgeleitete klasse an eine variable vom abstrakten basistyp gebunden wird, dieser mit aufgerufen wird und er deshalb definiert sein muss. ist das so?



  • define_ schrieb:

    und wieso muss der abstrakte destruktor einen funktionskörper haben? ich könnte mir denken, dass dieser für den fall, dass eine abgeleitete klasse an eine variable vom abstrakten basistyp gebunden wird, dieser mit aufgerufen wird und er deshalb definiert sein muss. ist das so?

    Ganz einfach: Ein Destruktor muss immer einen Funktionskörper haben, weil der Destruktor beim Zerstören immer aufgerufen wird (bei Klassenhierarchien von aussen nach innen, sprich von most-derived nach least-derived). Das gilt für den automatisch erzeugten Destruktor, warum sollte es beim expliziten Destruktor anders sein?



  • /edit: Später als der Adelige, war auf define_ bezogen.

    Ich weiß nicht, was du damit sagen wolltest, aber es liegt einfach daran, dass die Basisklasse auch Daten hat, die aufgeräumt werden wollen, völlig unabhängig davon, ob sie abstrakt ist oder nicht. Und dafür braucht's halt nen Destruktor, der somit immer vorhanden ist. Der Compiler macht dafür sowas:

    class Base
    {
    public:
        virtual ~Base () = 0;
    };
    
    Base::~Base ()
    {
    }
    


  • Wobei sich die Frage aufdrängt was ein rein virtueller dtor dann bringt, also wieso man das überhaupt machen kann.

    Die IMO einzig sinnvolle Antwort ist: Symmetrie. Wenns mit normalen Funktionen geht sollte es mit dem dtor auch gehen.



  • Der rein virtuelle Destruktor sorgt dafür, dass die Klasse abstrakt ist, auch wenn sie keine sonstigen rein virtuellen Funktionen enthält. Das ist doch auch was 🙂



  • LordJaxom schrieb:

    Der rein virtuelle Destruktor sorgt dafür, dass die Klasse abstrakt ist, auch wenn sie keine sonstigen rein virtuellen Funktionen enthält. Das ist doch auch was 🙂

    Das ist genau der Punkt: gerade wenn man verhindern will, dass eine Klasse, die ansonsten nur "normal"-virtuelle Funktionen hat, konkret benutzt wird, macht man den Destruktor pure virtual. Natürlich kann man dann einfach eine ansonsten leere Klasse von dieser abstrakten Klasse ableiten und erhält im Grunde das Selbe. Der Unterschied ist aber, dass man dann keine konkret benutzte Klasse innerhalb der Klassenhierarchie hat, sondern immer nur am Ende. (Ich mein Myers hätte dazu was in einem seiner effective C++ Bücher geschrieben.)
    Außerdem ist es häufig Konvention, zuerst die Konstruktoren, dann den Destruktor und erst danach andere Methoden zu deklarieren, damit ist das die erste Möglichkeit, eine Klasse als abstrakt zu kennzeichnen. Grade bei großen Klassen mit vielen Methoden ist das übersichtlicher als darauf zu vertrauen, dass jemand das "= 0" in Zeile 5276 bemerkt 😉



  • LordJaxom schrieb:

    Der rein virtuelle Destruktor sorgt dafür, dass die Klasse abstrakt ist, auch wenn sie keine sonstigen rein virtuellen Funktionen enthält. Das ist doch auch was 🙂

    Daran hab ich auch zuerst gedacht.
    Allerdings fällt mir einfach kein Anwendungsfall ein wo das Sinn machen würde.



  • hustbaer schrieb:

    Daran hab ich auch zuerst gedacht.
    Allerdings fällt mir einfach kein Anwendungsfall ein wo das Sinn machen würde.

    Nehmen wir an du hast eine Basisklasse die eine Handvoll virtuelle Funtkionen deklariert, bzw. sogar alle definiert weil sie sinnvolle default-Implementationen haben. Du willst ableitende Klassen nicht zwingen, irgendeine dieser Methoden zwangsweise zwangsweise zu überladen und ggf. nur die default-Implementation der Basisklasse aufzurufen, daher kannst du guten Gewissens kaum eine der Methoden pur virtuell machen. Der einzige Weg, die Klasse abstrakt zu machen ist daher der pur virtuelle Destruktor.
    Wie oben angedeutet könnte man jetzt bemerken dass man die Klasse dann auch so benutzen kann wie sie ist, da ja alle Implementierungen sinnvoll sind, allerdings wird eben oft gesagt, dass man nur Objekte von Klassen am Ende der Hierarchie konkret benutzen sollte.

    //Basisklasse, sollte man nicht unbedingt Objekte von erstellen -> abstrakt
    class AbstractActor
    {
    public:
      ~AbstractActor() {} = 0; //abstract
      virtual void DoSomeFoo()
      {
         Foo f;
         f.DoSomething();
      }
      virtual void DoSomeBar()
      {
         Bar b;
         b.DoSomething();
      }
    };
    
    //spezielle Version von DoSomeBar()
    class SpecialBarActor : public AbstractActor
    {
    public:
      virtual void DoSomeBar()
      {
         Bar b1, b2;
         b1.DoSomething();
         b2.DoSomething();
      }
    };
    
    class SpecialFooActor : public AbstracActor
    {
      /* Du kannst es dir denken...*/
    };
    
    //Standardklasse, Verhalten komplett defaultmaessig
    class NormalActor : public AbstracActor
    {};
    


  • hustbaer schrieb:

    Wobei sich die Frage aufdrängt was ein rein virtueller dtor dann bringt, also wieso man das überhaupt machen kann.

    Der "pure virtual destructor" garantiert, daß man über einen Basiszeiger dieser Klassenhierachie Objekte löschen kann. Wenn der Destructor nicht virtuell ist, dann wird der falsche Destruktor aufgerufen und das sorgt für Ärger. Wenn es eine abstrake Basisklasse ist, dann macht man den Destruktor halt "pure virtual". Eine Default Implementation kann man dem Destruktor immer noch mitgeben.



  • Eine Default Implementation kann man dem Destruktor immer noch mitgeben.

    Muss man. Nicht kann man.
    Simon



  • ~john schrieb:

    hustbaer schrieb:

    Wobei sich die Frage aufdrängt was ein rein virtueller dtor dann bringt, also wieso man das überhaupt machen kann.

    Der "pure virtual destructor" garantiert, daß man über einen Basiszeiger dieser Klassenhierachie Objekte löschen kann. Wenn der Destructor nicht virtuell ist, dann wird der falsche Destruktor aufgerufen und das sorgt für Ärger. Wenn es eine abstrake Basisklasse ist, dann macht man den Destruktor halt "pure virtual". Eine Default Implementation kann man dem Destruktor immer noch mitgeben.

    Manchmal gehst du mir kräftig damit auf den Sack dass du mir Dinge erklärst die ich a) schon weiss und die b) überhaupt nicht zum Thema gehören. Von virtuell oder nicht virtuell war nie die Rede, es ging um virtual pure oder bloss virtual.

    Und wenn die Klasse sowieso schon abstrakt ist braucht man den dtor nicht pure zu machen weil es genau garnix ändert.

    @pumuckl:
    Genau das meine ich ja, wenn man keine der Funktionen pure machen will, dann kann man die Klasse gleich instanzierbar lassen.

    Mir ist schon klar was passiert wenn man den dtor pure macht. Bloss wie gesagt sehe ich keinen Fall wo es IMO Sinn machen würde.



  • hustbaer schrieb:

    Manchmal gehst du mir kräftig damit auf den Sack dass du mir Dinge erklärst die ich a) schon weiss und die b) überhaupt nicht zum Thema gehören.

    Um bei Deiner blumigen Aussprache zu bleiben: Mir gehen Leute auf den Sack, die nicht aufmerksam sind.

    Die minimalste abstrakte Basisklasse einer polymorphen Klassenhierachie ist

    class Base {
    public:
        virtual ~Base() = 0;
    };
    

    Wenn man das "pure" für den Destruktor verböte, dann wären keine leeren abstrakten Basisklassen mehr möglich. In "The Design an Evolution of C++" begründet Stroustrup, warum er damals die Entscheidung so traf wie er es tat.



  • simon.gysi schrieb:

    Muss man. Nicht kann man.
    Simon

    Man muß ihn implementieren, aber den möglichen leeren Destruktor würde ich nicht als "Default" bezeichnen.



  • hustbaer schrieb:

    @pumuckl:
    Genau das meine ich ja, wenn man keine der Funktionen pure machen will, dann kann man die Klasse gleich instanzierbar lassen.

    Wie erwähnt wird häufiger die Auffassung vertreten, dass polymorphe Basisklassen immer abstrakt sein sollten. Wenns woanders nicht möglich ist muss also der Dtor dafür herhalten.

    hustbaer schrieb:

    Und wenn die Klasse sowieso schon abstrakt ist braucht man den dtor nicht pure zu machen weil es genau garnix ändert.

    Wenn man sich an die Konvention hält, in abstrakten Klassen auf jeden Fall den Dtor pure virtual zu halten, hat man immer einen Anhaltspunkt wo man nachschauen kann.

    Man muss den pur virtuellen Dtor also nicht benutzen, man käme auch ganz ohne aus. Er bietet aber die Möglichkeit, Konventionen umzusetzen (Basisklasse = abstrakt, abstrakt = pv-Dtor).



  • Und was ist, wenn ich als Anwender eine abgeleitete Klasse mache, die schon mit dem Standarddestruktor sauber aufgeräumt wird (was bei dem RAII-lastigen Stil heutiger C++-Programme gar nicht so selten vorkommt)?
    Dann ist die obligatorische virtual ~Derived () {} nur Syntaxlärm.

    Ich sehe auch noch nicht so ganz den Sinn darin, den Destruktor rein virtuell zu machen.



  • .filmor schrieb:

    Und was ist, wenn ich als Anwender eine abgeleitete Klasse mache, die schon mit dem Standarddestruktor sauber aufgeräumt wird (was bei dem RAII-lastigen Stil heutiger C++-Programme gar nicht so selten vorkommt)?

    Dann brauchst Du genau garnichts unternehmen, weil der vom Compiler generierte Standard-Dtor ebenfalls eine Implementierung des pure virtual Destruktors ist.

    struct Abstract
    {
       virtual ~Abstract() = 0;
    };
    
    struct Concrete : Abstract
    {
    };
    
    Concrete instance;
    

Anmelden zum Antworten