rein virtulle Destruktoren



  • Hey Leute,

    kurze Frage, warum müssen rein virutelle Destruktoren stets definiert werden, auch wenn ihr Definitionsrumpf evtl. leer ist?

    In Code ausgedrückt:

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

    Warum bekommt man ohne

    A::~A() {}
    

    eine Fehlermeldung?

    Ich vermute, dass dies daran liegt, dass man den Destruktor von A in keiner Unterklasse überschreiben kann und man hier eigentlich nur ausdrücken möchte, dass die Klasse abstrakt ist, ohne auf die Definition einer anderen Memberfunktion von A mit einer default-Implementierung oder ähnlichem verzichten zu müssen.

    Trotzdem erscheint mir das Ganze etwas kommisch, da dass "=0" eigentlich gerade ausdrücken will, dass diese Funktion keine Definition haben soll und man daraus aus erst schließen will, dass die Klasse abstrakt ist.

    Viele Grüße 🙂



  • Die Destruktoren der abgeleiteten Klassen rufen den Destruktor der Basisklasse auf, also muss er definiert sein. Das =0 hat dann nur den Effekt, die Klasse abstrakt zu machen.



  • Vielen Dank für deine Antwort 🙂

    Die Destruktoren sind aber der einzige Fall, die definiert werden müssen, obwohl sie als "=0" gekennzeichnet sind oder?

    Gruß 🙂



  • Jain.
    Klassisches Beispiel:

    class A {
    void Init() = 0;
    }
    
    A:A() {
      Init();
    }
    

    Da abgeleitetete Klassen nicht definiert sind, versucht er A:Init() aufzurufen. So ein Konstrukt macht aber im Normallfall keinen Sinn.



  • Nicht-virtuelle Funktionen können nicht abstrakt sein. Vielleicht hast du das virtual vergessen.

    Da abgeleitetete Klassen nicht definiert sind, versucht er A:Init() aufzurufen.

    Es ist vollkommen egal, was in den abgeleiteten Klassen ist, er ruft in jedem Fall A::Init auf. Deswegen macht es auch keinen Sinn, diese Funktion virtual zu machen.



  • Wenn du in einem Konstruktor eine virtuelle Methode aufrufst, wird nicht die Überladung aufgerufen, die das endgültige Objekt haben wird, sondern die, die zu der Klasse gehört, in deren Konstruktor du dich gerade befindest. In deinem Beispiel (auch wenn Init virtual deklariert wäre, was vtml. deine Absicht war) würde der Compiler gar nicht erst versuchen, die Methode der Kindklasse aufzulösen sondern gleich A::Init zu benutzen versuchen.

    Das liegt daran, dass *this zu diesem Zeitpunkt noch kein Objekt der Kindklasse ist. Eventuell durch abgelittene Klassen neu hinzugekommene Objektteile sind zu diesem Zeitpunkt noch nicht konstruiert, und eine virtuelle Methode muss sich darauf verlassen können, mit einem vollständigen Objekt seiner Klasse umzugehen.



  • Ich hab mich nur unglaublich schlecht ausgedrückt. Meinte eigentlich genau das, was ihr sagt. ^^



  • Ich bin jetzt etwas verwirrt 😞

    Wie kann man eine rein virtuelle Funktion aufrufen (abgesehen vom Destruktor), auch wenn man sie außerhalb der Klasse definiert ?

    Angenommen man hat so etwas wie:

    class A
    {
      public:
        virtual void foo() = 0;
      /* ... */
    };
    
    void A::foo()
    {
      /* DO SOMETHING */
    }
    

    Obwohl A::foo() jetzt definiert ist, kann man es doch nicht schaffen irgendwie das "DO SOMETHING" auszuführen oder?

    Gruß 🙂



  • MatheStein schrieb:

    Wie kann man eine rein virtuelle Funktion aufrufen

    Genauso wie nicht rein virtuelle Funktionen.



  • Hast du vllt ein Beispiel?

    In der abstrakten Klasse selbst geht glaube ich nicht, da man diese nicht instanziieren kann und in Unterklassen geht es denke ich nicht, da dort das foo wegen dem "=0" überschrieben sein muss und wegen "virtual" die Implementation aus der Unterklasse genommen wird. So sehe ich das zumindest bis jetzt ^^

    Gruß 🙂



  • MatheStein schrieb:

    Hast du vllt ein Beispiel?

    OK, deine Überlegungen sind richtig, über den normalen virtuellen Mechanismus kriegt man sie nicht zu fassen. Man kann diesen aber umgehen, indem man die Klasse dazu angibt:

    A::foo();
    

    Oder halt im Konstruktor, wie schon erwähnt.



  • Übliche Beispiel ist das Aufrufen aus der abgeleiteten Methode:

    class B : public A
    {
      public:
        virtual void foo();
    };
    
    void B::foo()
    {
      A::foo();
    }
    

    Aber du kannst auch jederzeit dann das A::foo() aufrufen, z.B.:

    B b;
    
    b.A::foo();
    

    Ausführliches Beispiel unter http://ideone.com/1T0Yj



  • Besten Dank Leute, jetzt habe ich es verstanden 🙂

    Eine abschließende Frage noch. Kann man so etwas irgendwie elegant verkürzen:

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

    Ich meine mal so etwas ähnliches wie

    virtual ~A() {} = 0;
    

    gesehe zu haben, aber das kompiliert bei mir nicht.

    BTW:
    Das man den Destruktor an dieser Stelle explizit definieren muss liegt daran, dass man ihn deklariert hat durch

    virtual ~A() = 0;
    

    und somit kein Standard-Destruktor mehr vom Compiler zur Verfügung gestellt wird oder?

    Gruß 🙂



  • seldon schrieb:

    Wenn du in einem Konstruktor eine virtuelle Methode aufrufst, wird nicht die Überladung aufgerufen, die das endgültige Objekt haben wird, sondern die, die zu der Klasse gehört, in deren Konstruktor du dich gerade befindest

    Das aufrufen einer virtuellen Methode im Konstruktor ist undefiniert. Was tatsaechlich passiert ist kompilerabhaengig. Einfache Regel: Keine virtuellen Methoden im Konstruktor aufrufen.



  • knivil schrieb:

    Das aufrufen einer virtuellen Methode im Konstruktor ist undefiniert. Was tatsaechlich passiert ist kompilerabhaengig. Einfache Regel: Keine virtuellen Methoden im Konstruktor aufrufen.

    Kompletter Bullshit.



  • Bashar schrieb:

    knivil schrieb:

    Das aufrufen einer virtuellen Methode im Konstruktor ist undefiniert. Was tatsaechlich passiert ist kompilerabhaengig. Einfache Regel: Keine virtuellen Methoden im Konstruktor aufrufen.

    Kompletter Bullshit.

    Genau.

    Es wird immer die Variante aufgerufen, die zum "aktuellen" Objekttyp gehört.
    Ab dem Zeitpunkt wo Konstruktor A::A anfängt "eigenen" Code auszuführen, ist das Type "A".

    Auch wenn es eine Klasse B gibt, die von A abgeleitet ist, und man ein Objekt vom Typ "B" erzeugt.

    So lange Konstruktor B::B nicht fertig ist mit dem Aufrufen der Konstruktoren der Basisklassenobjekte, und angefangen hat "eigenen" Code auszuführen, ist das B nämlich noch kein B, sondern ein A.

    MatheStein schrieb:

    Ich meine mal so etwas ähnliches wie

    virtual ~A() {} = 0;
    

    gesehe zu haben, aber das kompiliert bei mir nicht.

    Vertausch mal das "= 0" mit dem "{}".



  • knivil schrieb:

    MatheStein schrieb:

    Ich meine mal so etwas ähnliches wie

    virtual ~A() {} = 0;
    

    gesehe zu haben, aber das kompiliert bei mir nicht.

    Vertausch mal das "= 0" mit dem "{}".

    Klappt leider auch nicht 😞



  • Die Sprachgrammatik lässt das schlicht und einfach nicht zu.



  • Ich versteh da auch den Sinn gar nicht. Eine pure-virtual-Funktion sorgt dafür, dass Instanzen dieser Klasse nicht möglich sind, und vor allem, dass in abgeleiteten Klassen diese Funktion implementiert werden muss (oder als pure-virtual weitergereicht). Welchen Sinn macht es jetzt, in einer Basisklasse festzulegen, dass abgeleitete Klassen den Destruktor implementieren müssen? Und wenn es nur um die Instanziierung geht, warum nicht eine normale pure-virtual-Funktion nehmen? Wenn das keinen Sinn macht, was ist der Hintergrund, warum du Instanzen verbieten willst?



  • Bashar schrieb:

    Kompletter Bullshit.

    Na ich hab es nicht so mit dem Standard, aber vielleicht kannst du ja die entsprechende Stelle posten.

    Edit: Ok. hab es nachgelesen. Nein, nicht im Standard.


Anmelden zum Antworten