Zugriff auf abstrakte Methoden



  • Hier eine kleines handliches Interface (Abstrakte Klasse):

    class AbstractGameState
    {
    public:
        virtual void Initialize() = 0;
        virtual void Shutdown() = 0;
        virtual void Update() = 0; //Core game logic
        virtual void Draw() = 0; //Handle Ogre's thread, tell OgreManager what to draw, what has moved, etc.
    	virtual ~AbstractGameState() {}
    };
    

    Wie ihr seht ist als einziges der Destruktor implementiert. Alles andere muss von seinen Kindern implementiert werden.
    Jetzt möchte ich aber gerne, dass jeder Destruktor von den Kinderklassen(wenn er nicht überschrieben wird) die Funktion Shutdown() aufruft.

    virtual ~AbstractGameState() { this->Shutdown(); }
    

    gibt aber leider einen Linker Error:

    1>MainGame.obj : error LNK2001: Nicht aufgelöstes externes Symbol ""public: virtual void __thiscall AbstractGameState::Shutdown(void)" (?Shutdown@AbstractGameState@@UAEXXZ)".
    

    Verständlich, da die Funktion in dieser Klasse nicht implementiert wird.

    Gibt es eine Methode das von mir erwünschte zu erreichen ohne ~AbstractGameState() in jeder Kinderklasse erneut zu implementieren?



  • AlphaWolf schrieb:

    Gibt es eine Methode das von mir erwünschte zu erreichen ohne ~AbstractGameState() in jeder Kinderklasse erneut zu implementieren?

    ja. du lässt den code zum beenden im destruktor.

    selbst wenn du den base-destruktor die funktion shutdown aufrufen lässt, ist zu dem zeitpunkt, an dem der base-destruktor aufgerufen wird, das abgeleitete objekt schon destruiert - d.h. es gibt es nicht mehr. daher auch deine fehlermeldung: der linker verlinkt in dem destruktor von base die funktion Base::Shutdown - aber die hast du nicht implementiert. (selbst wenn du sie implementieren würdest, was durchaus möglich ist, würde das dein problem nicht lösen)



  • Ich habe schon extra eine Shutdown() und Initialize() Funktion gemacht. Die verschiedenen States sollen geladen und entladen werden können ohne das Objekt zu destruieren.

    Deine Erklärung leuchtet ein und zeigt mir, dass es so nicht geht. Ich werde dann auf den Aufruf von Shutdown() im Destruktor verzichten. Ich wollte da nur eine zusätzliche Sicherheit schaffen auf die ich aber auch verzichten kann.



  • AlphaWolf schrieb:

    Jetzt möchte ich aber gerne, dass jeder Destruktor von den Kinderklassen(wenn er nicht überschrieben wird)
    ...
    ohne ~AbstractGameState() in jeder Kinderklasse erneut zu implementieren?

    Bei virtuellen Destruktoren ist das ein bisschen anders, im Grunde genommen kannst du sie in den abgeleitenten Klassen gar nicht überschreiben.
    Du erzwingst ja mit einem virtuellen BasisDestruktor nur, dass die Destruktoren der abgeleiteten Klasse, diesen auch aufrufen. ( Im Fall von Polymorphie )



  • Verstehe. Wäre er nicht virtuell würde immer nur jeweils der Destruktur der abgeleiteten Klasse aufgerufen der der base-Klasse aber ignoriert. Richtig?

    Bei Konstruktoren verhält es sich doch so, dass man die der base-Klasse immer manuell in der abgeleiteten Klasse aufrufen muss und man diese deshalb auch nicht als virtuell deklarieren muss/kann. Richtig?

    Ich habe das alles mal durch ausprobieren rausgefunden aber mangels Praxis wieder vergessen.



  • AlphaWolf schrieb:

    Verstehe. Wäre er nicht virtuell würde immer nur jeweils der Destruktur der abgeleiteten Klasse aufgerufen der der base-Klasse aber ignoriert. Richtig?

    Genau anders rum. Wenn der Destruktor nicht virtuell ist und du deletest einen Pointer auf Base, wird beim nichtvirtuellen Dtor nur der Basisdestruktor aufgerufen, nicht aber der der abgeleiteten Klasse. "And we enter the shady realms of undefined beaviour"

    Insofern ist KasFs Aussage auch nicht so ganz richtig. Wenn du ein Objekt der Abgeleiteten KLasse zerstörst, wird natürlich auch der Destruktor der Basisklasse aufgerufen, egal ob virtuell oder nicht. Nur wenn du ein Objekt einer abgeleiteten KLasse über einen Pointer auf die Basisklasse behandelst und zerstörst, muss das Programm zur Laufzeit ja irgendwie feststellen, dass es garkein Base objekt sondern ein Derived Objekt zerstören muss.


  • Mod

    Kleine Anmerkung: es ist nicht undefiniert, nur den Basisdestruktor aufzurufen - Es ist schließlich auch nicht undefiniert, den Destruktor überhaupt nicht aufzurufen und auch ein Subobjekt ist ein richtiges Objekt, das zerstört werden kann:

    struct A {};
    struct B : A {};
    
    A* p = new B;
    p->~A(); // ok, ruft nur den Basisdestruktor auf
    operator delete(p); // und Speicher freigeben, aber nicht:
    delete p; // undefiniert
    

    Undefiniert ist die Anwendung des delete-Operators selbst. Hauptgrund dafür ist, dass ein der delete-Operator die korrekte Deallokationsfunktion aufrufen muss. Diese könnte aber in B überladen worden sein, ein delete auf einen Zeiger der Basisklasse muss aber in der Lage sein, diese Überladung zu finden. Es wird also polymorphes Verhalten gefordert, obwohl die Deallokationsfunktion selbst nicht virtuell ist (sie hat ja nicht mal einen Objektparameter und das eigentliche Objekte existiert ja beim Aufuruf der Deallokationsfunktion schon nicht mehr, sein Typ ist dann nicht mehr bestimmbar) - die Lösung sieht daher (in der Praxis) so aus, dass der Destruktor für die Auswahl der richtigen Funktion verantwortlich ist, und die Sprachdefinition fordert kategorisch einen virtuellen Destruktor.
    Ein interessanter (wenn auch vermutlich nutzloser) Aspekt dabei ist, dass man Fälle konstruieren kann, in denen ein Objekt nur über den Zeiger auf die Basisklasse gelöscht werden kann:

    struct A { virtual ~A() {} };
    struct B : A { private: static operator delete(void*); };
    
    B* p = new B;
    delete p; // geht nicht, da Zugriffskontrolle sowohl für den Destruktor als auch für die Deallokationsfunktion erfolgt
    A* q = p;
    delete q; // ok
    

    (es dürfte aber sinnvoller sein, so etwas über den Destruktor zu bewirken).



  • pumuckl schrieb:

    Insofern ist KasFs Aussage auch nicht so ganz richtig.

    Uuups, hast natürlich Recht. Habs falsch formuliert 🙂 ...

    KasF schrieb:

    Du erzwingst ja mit einem virtuellen BasisDestruktor nur,...

    ... dass bei der Destruktion eines eines Objekt über einen Basisklassenzeiger/referenz auch zusätzlich zuerst der Destruktor der abgeleiteten Klasse und danach der Basis aufgerufen wird, anstatt ohne virtuell nur der Basisdestruktor.


Anmelden zum Antworten