"pure virtual destructor"



  • ~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;
    


  • .filmor schrieb:

    obligatorische virtual ~Derived () {} nur Syntaxlärm.

    Da in der Basis der Dtor schon virtuell ist ist es der automatisch gelieferte Dtor in der abgeleiteten Klasse auch, da ist also garnichts obligatorisch. Siehe NormalActor in meinem Beispiel oben, ganz ohne Lärm.



  • Na dann … 😉



  • Also die Frage nach dem Sinn stellt sich mir auch bissi ...

    der eigentliche zweck von pure virtual member, den ableiter drauf hinzuweisen, dass er selber was implementieren muss, greift ned, da destruktoren generiert werden wenn ned vorhanden.

    weiterhin, klassen, die technisch gesehen ned anders abstrakt gemacht werden koennen, aber abstrakt sein sollten, da faellt mir ned viel ein zu.
    - Leere interfaces wird wohl keiner bauen oder ?

    Bleibt irgendwie einzig das Argument, das man vielleicht in ner Hirarchie immer nur das ende instanzieieren sollte, und weiss ned ob das guter stil ist ... zumal es sich doch so einfach umgehen laesst. wenn ich an die def komme (sonst koennt ich ned instanzieren) kann ich auch ableiten ...

    Viele mir nur noch das thema RTTI, vielleicht in verbindung mit serializierung ein, aber dann waer das ableiten doch eigentlich nur ne weitere info, was sich irgendwie eleganter loesen lies ...

    Hat das wirklich swchon mal jemand gebraucht ?

    Ciao ...



  • RHBaum schrieb:

    Bleibt irgendwie einzig das Argument, das man vielleicht in ner Hirarchie immer nur das ende instanzieieren sollte, und weiss ned ob das guter stil ist ... zumal es sich doch so einfach umgehen laesst. wenn ich an die def komme (sonst koennt ich ned instanzieren) kann ich auch ableiten ...

    Mit dem Ableiten hast du dann aber eine Klasse geschaffen, die am Ende der Hierarchie steht, im Gegensatz zu deiner Basisklasse, auch wenn sie dieselbe Funktionalität hat. Siehe mein Beispiel oben.
    Es mag so aussehn als ob es nicht besonders sinnvoll ist, zwei Klassen mit derselben Funktionalität zu haben, von der die eine abstrakt ist und die andere nicht. Hier wurde schon das eine oder andere Mal gesagt "dann instantiiere ich doch einfach die Basisklasse". Es ist aber zu beachten, dass polymorphe Basisklassen und konkrete Klassen zwei verschiedene Aufgaben haben: Basisklassen bieten ein Interface das allen konkreten Klassen gemeinsam ist, konkrete Klassen sind dazu da, Objekte davon zu schaffen. Deshalb sollte man beides trennen, auch wenn mal eine konkrete Klasse exakt das Verhalten der Basisklasse haben sollte.
    Im Laufe der Entwicklung kann es vorkommen dass das Verhalten einer konkreten Klasse angepasst werden muss. Wenn ich konkrete Klasse und Basisklasse nicht getrennt habe, muss ich also eine neue Klasse ableiten und überall dort wo ich die Basisklasse konkret benutzt habe stattdessen die Ableitung benutzen. Das ist viel Textersetzung, bevor ich alles rekompiliere. Wenn ich dagegen von Anfang an die beiden Konzepte Basisklasse und konkrete Klasse getrennt halte, schreibe ich lediglich die konkrete Klasse um und rekompiliere - ohne überall Textersetzugen vorzunehmen. Noch komplizierter wirds, wenn man in verschiedenen Kontexten erstmal die selbe Funktionalität braucht wie die Basisklasse und dann in einem der Kontexte das Verhalten ändern muss - dann müsste man die Basisklasse an einzelnen Stellen (im Kontext wo sich das Verhalten ändert) ersetzen durch eine neue abgeleitete Klasse, an anderen Stellen (im anderen Kontext) aber nicht. Das Chaos ist vorprogrammiert. Stattdessen kann man von Anfang an für jeden Kontext eine leere Klasse von der Basisklasse ableiten und ist für solche Veränderungen bestens gewappnet.



  • Basisklassen bieten ein Interface das allen konkreten Klassen gemeinsam ist, konkrete Klassen sind dazu da, Objekte davon zu schaffen.

    Ja richtig ...
    Aber genau dann sollte man das auch so trennen, und ich wuerd die ganze hirarchie als interface pure abstract machen
    Dann sind die interfaces sauber und jede weitere ableitung in der hirarchie bringt nur weitere pure virtuale methoden mit sich ...

    Was intern dann benutzt um ne implementation deiner interfaces zur verfuegung zu stellen, ist dann ne ganz andere schiene ....

    Man sollte halt auch interface von Impl trennen, das halt bei themen mit standardfunktionalitaet nimmer der fall ...

    Basisklassen bieten ein Interface das allen konkreten Klassen gemeinsam ist, konkrete Klassen sind dazu da, Objekte davon zu schaffen

    Die vorraussetzung fuer die technik selber ist aber schon die verletzung an sich ... in dem du klassen hasst, die halb interface, halb Impl sind.
    Also einfach dem ding nen eigenes erweitertes interface(pure abstrakt) fuer die virtuellen funktionen um die es die Schnittstelle erweitert, und schon hasst wieder ne trennung .. und niemand kann jemanden vorwuerfe wegens design machen, weil wer deine "Halbe Impl die aber vollstandig funktioniert" direkt instanziiert.

    Ciao ...



  • Hm da hab ich mich wohl etwas Mißverständlich ausgedrückt. Mit dem Interface wraen in dem Fall nicht nur Funktionsdeklarationen gemeint, also keine Interface-Klasse wie man sie aus Java kennt, sondern eben alles was eine Basisklasse in einer polymorphen Klassenhierarchie ausmacht.
    Die Eigenschaft, polymorphe Basisklasse zu sein sowie die Eigenschaft, konkrete Klasse zu sein sind eben nach Auffassung vieler Leute voneinander zu trennen und daher Basisklassen in polymorphen Hierarchien grundsätzlich abstrakt zu halten.



  • wieso will man erzwingen, dass eine klasse abstrakt ist? damit's schön aussieht, oder hat das einen technischen grund? die fehlende möglichkeit der instanzierung einer abstrakten klasse ist kein argument, da man das auch mit protected ctors schafft.

    pure virtual dtors, die durch die default dtors in den abgeleiteten klassen implementiert werden, erzeugt eine inkonsistenz beim begriff "pure virtual". protected dtors erzeugen keine inkonsistenzen.



  • warum sollte man jemanden zwingen einen dtor neu als public zu definieren? wenn man eine klasse hat, die nur raii-container und pod als daten hat, wäre das schließlich überflüssig.

    oder anders: es ist geschmackssache.



  • ghorst schrieb:

    warum sollte man jemanden zwingen einen dtor neu als public zu definieren?

    sorry. ich hab mich einmal verschrieben. es sollte lauten: "protected ctors erzeugen keine inkonsistenzen." ctors. nicht dtors. man sollte wohl nicht so viel abkürzen 🙂



  • besserwisser schrieb:

    wieso will man erzwingen, dass eine klasse abstrakt ist? damit's schön aussieht, oder hat das einen technischen grund? die fehlende möglichkeit der instanzierung einer abstrakten klasse ist kein argument, da man das auch mit protected ctors schafft.

    1. Weil es die übliche C++ Vorgehensweise ist?
    2. Um auch anderen im Projekt sofort sichtbar zu machen das dies nur als Basisklasse gedacht ist (Es kann ja sonst vielleicht noch einer auf den Gedanken kommen eine Factorymethode zur Verfügung zu stellen)

    Kurz gesagt: es ist eindeutiger.



  • asc schrieb:

    1. Weil es die übliche C++ Vorgehensweise ist?
    2. Um auch anderen im Projekt sofort sichtbar zu machen das dies nur als Basisklasse gedacht ist (Es kann ja sonst vielleicht noch einer auf den Gedanken kommen eine Factorymethode zur Verfügung zu stellen)

    1: damit es also wirklich schöner aussieht.
    2: das kann aber ebenso ein nachteil sein. außerdem muss die factory methode sowieso als teil der klasse deklariert werden. damit ist maximal der ersteller der klasse auch der, der die factory methode schreibt und nicht jemand externer.

    ich will ja nicht behaupten, dass die methode mit dem pure virtual dtor grundsätzlich schlecht wäre. sie widerspricht nur einfach dem begriff pure virtual.

    die günstigere lösung wäre gewesen, das wort schlüsselwort abstract einzufühen. es hätte statt pure virtual für methoden verwendet und vor die klasse selbst geschrieben werden können. das würde irgendwelche hacks bezüglich des dtors unnötig machen und eine klasse klar als abstrakt deklarieren.
    mir ist schon klar, dass das zuweisen der 0 zu einer virtual methode die symbolik hat, 0 an diese stelle der vtable zu schreiben. ich bin aber der meinung, dass man mehr auf leserlichkeit und weniger auf symbolik wert legen sollte.

    aber ok, das wird ein glaubenskrieg und ich will mich hier mit den c++ soldaten nicht anlegen. das artet schnell in schlachten aus. 🙂 das war nur mein senf dazu.



  • besserwisser schrieb:

    2: das kann aber ebenso ein nachteil sein. außerdem muss die factory methode sowieso als teil der klasse deklariert werden. damit ist maximal der ersteller der klasse auch der, der die factory methode schreibt und nicht jemand externer.

    Nein... Nehme eine Klasse mit protected Ctor, leite davon ab und in der abgeleiteten Klasse kannst du ohne Probleme eine Factorymethode für die Basisklasse definieren.

    class CantInstantiate 
    {
      protected:
      CatInstantiate() {};
    /*...*/
    };
    
    class YesICan : private CantInstantiate
    {
    public:
      CantInstantiate* InstantiateIt()
      {
        return new CantInstantiate();
      }
    };
    

    Die einzigen Möglichkeiten, um eine Klasse vor Instantiierungen zu schützen, sind ein protected Destructor und pure virtuals. Der Protected Dtor fällt bei Laufzeitpolymorphie aber aus, da man sonst nicht mehr über Basisklassenpointer delete aufrufen kann.



  • hmm ok. das stimmt. ein weiterer grund für abstract als keyword.



  • besserwisser schrieb:

    hmm ok. das stimmt. ein weiterer grund für abstract als keyword.

    Wozu? Wozu ist das wirklich nötig?

    class A {
        public:
            virtual ~A() {} = 0; // Und schon "abstract"
    };
    

Anmelden zum Antworten