Wozu abstrakte Basisklassen?



  • camper schrieb:

    Michael E. schrieb:

    template<class Stream>
        virtual Stream& serialize(Stream& stream) = 0;
    

    ➡ Templatefunktionen können und dürfen nicht virtuell sein.

    Wieder den Standard auswendig gelernt? 😉 :p



  • und grade wenn man viele hier Serializable hat, kann man alle auch wenn sie unterschiedlich sind in einem array (oder anderen iterierbaren Datentypen) unterbringen, und verwalten.

    Bei grafischer programmierung gibt es auch eine Anwendungsmöglichkeit, denn da kann man beispielsweise alle grafisch darstellbaren Klassen (Helden, Steine, etc) mit einer Schnittstelle verwalten.


  • Mod

    Michael E. schrieb:

    Wieder den Standard auswendig gelernt? 😉 :p

    Das sollte Grundwissen sein, wenn man mit solchen Konstrukten arbeitet - einerseits wird dich der Compiler sowieso drauf hinweisen, andererseits ist die Regel nicht wirklich überraschend.



  • @Michael E. ***einfacher gehts kaum?! Wenn ich mich dumm stell, also mal angenommen dann versteh das nicht mal ich. 😉

    Zur Frage:

    Stell dir vor du schreibst ein Grafikprogramm mit einer Pluginschnittstelle, die andere Entwickler für ihre Plugins nutzen können.
    Dann würdest du wahrscheinlich eine abstrakte Basisklasse, wie die folgende entwickeln:

    class IPlugin
    {
    public:
        virtual void ~IPlugin() = 0;
        virtual SUCCEEDED PluginTaskForImage(IMAGE **Image) = 0;
        ....
    };
    

    Der Pluginentwickler würde dann seine Pluginklasse von deiner "IPlugin" ableiten und hinterher bei deiner PluginAPI eine instanz dieser durch eine Funktion wie zB. "AddPlugin( IPlugin * Interface);" anmelden.

    Das würde dann so aussehen:

    class CPlugin : public IPlugin
    {
    public:
        CPlugin(...);
        void ~CPlugin();
        SUCCEEDED PluginTaskForImage(IMAGE **Image);
        ....
    };
    
    ...
    
    //zB. innerhalb der DllMain
    CPlugin *myPlugin = new CPlugin(...);
    PluginAPI::AddPlugin( static_cast<IPlugin*> myPlugin);
    

    MfG Wally



  • Danke für die vielen Antworten.

    Michael E. schrieb:

    Mit abstrakten Basisklassen kannst du Interfaces anbieten. In diesem Beispiel gebe ich Klassen, die von Serializable erben, eine Schnittstelle, mit deer ich sie serialisieren (z. B. in eine Datei speichern) kann.

    Erstmal habe ich nicht ganz verstanden, was du mit Plugins meinst, aber durch die nachfolgenden Erklärungen ist es mir klar geworden. 🙂

    Wally schrieb:

    Der Pluginentwickler würde dann seine Pluginklasse von deiner "IPlugin" ableiten und hinterher bei deiner PluginAPI eine instanz dieser durch eine Funktion wie zB. "AddPlugin( IPlugin * Interface);" anmelden.

    Jetzt hab ich es geschnallt. Der Grundgedanke ist also der, dass meine Plugin-API einen umkonvertierten Klassenzeiger der fremden (von der absktrakten Basisklasse abgeleiteten) Pluginklasse erhält, und dann die in der abgeleiteten Klasse redefinierten Methoden aufrufen kann, so als seien es die klasseneigenen Methoden, richtig?



  • Okay, das gibt mir die Bestätigung, es funktioniert:

    #include <iostream>
    #include <string>
    
    class AbstractBasis
    {
        public:
            virtual void Test (const std::string &text) = 0;
    };
    
    class DerivedClass : public AbstractBasis
    {
        public:
            void Test (const std::string &text) {
                std::cout << "Derived Class: " << text << std::endl;
            }
    };
    
    class AbstractAPI
    {
        public:
            void AddPlugin (DerivedClass *Handler) {
                Handler->Test ("Hallo, ich bins");
            }
    };
    
    int main ()
    {
        DerivedClass *derive = new DerivedClass;
        AbstractAPI api;
        api.AddPlugin (static_cast <DerivedClass*> (derive));
        std::cin.get ();
    }
    

    Danke an euch! 👍



  • Sorry, eine Unklarheit ist mir noch aufgekommen, es geht um folgendes:

    Die Klasse AbstractAPI währe in diesem Beispiel eine Klasse meines Grafikprogramms, die eine Instanz der abgeleiteten (->fremden) Klasse erwartet, und dann dessen Methoden aufrufen kann. Genau das passiert in dieser Zeile:

    void AddPlugin (DerivedClass *Handler) {
                Handler->Test ("Hallo, ich bins");
            }
    

    Allerdings muss in den Funktionsparametern von AddPlugin () der Objekttyp der fremden Klasse bekannt sein (in dem Fall DerivedClass), um die übergebene Instanz einen bestimmten Klassentyp zuzuordnen zu können. In diesem Programmbeispiel klappt das ja noch ganz gut, da ich ja DerivedClass kenne, die aber eigentlich die (unbekannte) Fremdklasse des Plugins ist. Woher soll nun AddPlugin wissen, welchen Objekttyp *Handler zugeordnet werden soll? Folgendes Beispiel klappt so nicht:

    void AddPlugin (void *Handler) {
                Handler->Test ("Hallo, ich bins");
            }
    

    Welche Lösung gibt es da?



  • Und woher weißt du wie die Funktion heißt die du ausführst?
    also

    void AddPlugin (DerivedClass *Handler) {
    Handler->Test ("Hallo, ich bins");
    }

    Test(char * sonstwas) ??



  • Und woher weißt du wie die Funktion heißt die du ausführst?

    Das ist ja nicht das Problem, er ruft lediglich die Funktionen auf, die er in seiner abstrakten Basisklasse vordefiniert hat, die anderen (ihm unbekannten) Funktionen der Klasseninstanz sind unrelevant. Nicht aber der Klassentyp 😉



  • Das ist ja nicht das Problem, er ruft lediglich die Funktionen auf, die er in seiner abstrakten Basisklasse vordefiniert hat, die anderen (ihm unbekannten) Funktionen der Klasseninstanz sind unrelevant. Nicht aber der Klassentyp 😉

    Achso ich fürchte da kann ich nicht helfen würde mich aber auch interessieren 😉



  • Genau dafür ist Polymorphie da. Du kannst folgendes schreiben:

    void AddPlugin (AbstractBasis* Handler) {
                Handler->Test ("Hallo, ich bins");
            }
    

    Dadurch, dass Handler ein Zeiger (ginge auch mit ner Referenz) ist, wird bei virtuellen Funktionsaufrufen erst mal der dynamische Typ des Objekts rausgefunden und dann die passende Funktion aufgerufen.



  • Dadurch, dass Handler ein Zeiger (ginge auch mit ner Referenz) ist, wird bei virtuellen Funktionsaufrufen erst mal der dynamische Typ des Objekts rausgefunden und dann die passende Funktion aufgerufen.

    Mensch, jetzt wird's mir auch klar... 💡 Hast ja Recht, manchmal sieht man den Wald vor lauter Bäumen nicht. Danke 🙂


Anmelden zum Antworten