Template Methode nur spezialisiert vererben



  • GorbGorb schrieb:

    @Skym0sh0: Hab ich das jetzt richtig verstanden, dass du sowas willst wie

    class Container
    {
        Base *ptr; //hier steckt irgendein Derived drin
        template< class Type >
        void init( Type Object )
        {
            ptr->init( Object );
            //Wenn Derived init(Type) überschrieben hat, dann soll das aufgerufen
            //werden, ansonsten soll nichts passieren.
        }
    };
    

    nein, sowas habe ich:

    class Base
    {
    public:
        template<typename T>
        virtual void init(T const& x) = 0;
    };
    
    class DeriveA : public Base
    {
    public:
        template<>
        virtual void init(String const& x) // braucht einen string für seine initialisierung
        {
            // do something
        }
    }
    
    class DeriveB : public Base
    {
    public:
        template<>
        virtual void init(std::vector<int> const& x) // braucht einen vector für seine initialisierung
        {
            // do something
        }
    }
    
    class DeriveC : public Base
    {
    public:
        template<>
        virtual void init(std::complex<double> const& x) // braucht eine complexe zahl für seine initialisierung
        {
            // do something
        }
    }
    
    class Manager
    {
        std::vector<Base*> vec; // wird auch irgendwo gefüllt mit den klassen...
    
    public:
        void init(int index)
        {
             if ( index == someThingSpecial1 )
                  vec[index]->init("String1");
             if ( index == someThingOtherSpecial )
                  vec[index]->init(std::vector<int>(100, 5);
             if ( index == lolFooBar )
                  vec[index]->init(std::complex<double>(5.0, 3.0);
        }
    };
    

    das ist nur schematisch gemeint, weder getestet noch kompilierbar und auch von der genauen implementierung meines projektes abweichend



  • Wenn du ohnehin eine Fallunterscheidung hast, solltest du statische Typinformation über die abgeleiteten Objekte besitzen. Dann kannst du auch gleich die richtige Init() -Methode aufrufen...

    Aber grundsätzlich sind solche Fallunterscheidungen fast immer ein Hinweis auf schlechtes Design. Was sind das semantisch für Klassen, was genau tut Init() ?



  • deshalb hab ich irgendwo auch gesagt, dass mir auffiel, dass es doch nicht so toll ist...

    init initialisiert das gewählte objekt, aber das braucht halt mal nen string, mal en array, mal sonstwas

    eine möglichkeit wäre als parameter einen zeiger auf eine andere klasse (oder strukt) mitzugeben und von eben dieser klasse(oder struct) spezialierungen zu erben um deren typen sich die objekte dann intern selbst kümmern können

    also so:

    struct BaseInfos
    {
    };
    
    class Base
    {
    public:
        virtual void init(BaseInfos * b) = 0;
    };
    
    struct DeriveAInfos : public BaseInfos
    {
        std::string s;
        int foo;
    };
    
    class DeriveA
    {
    public:
        virtual void init(BaseInfos * b)
        {
            DeriveAInfos * d = dynamic_cast<DeriveAInfos*>(b);
            doWhatever(d->s, d->foo);
        }
    };
    

    so wäre es denke ich was besser alles, oder?!



  • Sowas wie

    class Manager
    {
        std::vector<Base*> vec; // wird auch irgendwo gefüllt mit den klassen...
    
    public:
        void init(int index)
        {
             if ( index == someThingSpecial1 )
                  reinterpret_cast<DeriveA*>(vec[index])->init("String1");
             if ( index == someThingOtherSpecial )
                  reinterpret_cast<DeriveB*>(vec[index])->init(std::vector<int>(100, 5);
             if ( index == lolFooBar )
                  reinterpret_cast<DeriveC*>(vec[index])->init(std::complex<double>(5.0, 3.0);
        }
    };
    

    wäre nicht möglich? Du scheinst ja durch den Index schon zu wissen, was für ein spezieller Typ es ist, sonst könntest du ja gar nicht wissen, dass vec[index] ein Typ drin steckt, der diesen speziellen "Konstruktor" implementiert hat. Da kannst du auch genauso gut einfach eine ganz normale Memberfunktion aufrufen. Ich würde mich hier aber Nexus anschließen, das sieht mir nicht unbedingt nach gutem design aus.



  • Dynamischer Downcast ist selten besser 🙂

    Das Problem beginnt bereits früher. Wenn du weisst, dass du einen std::vector an Init() übergeben musst, kennst du ja auch das abgeleitete Objekt – schliesslich muss dieses mit dem std::vector etwas anfangen können. Das wiederum bedeutet, dass du nicht über die Basisklasse arbeiten musst, sondern direkt Derived::Init() aufrufen kannst.

    Erklär mal, wieso das nicht geht, du aber dennoch Fälle unterscheidest (durch die verschiedenen übergebenen Argumente wie std::vector , std::string etc.). Und dazu wäre es nützlich, die eigentliche Aufgabe der Klasse zu kennen, also nicht nur Base und Derived .



  • GorbGorb schrieb:

    reinterpret_cast
    

    Das wird richtig laut knallen. Nimm static_cast wenn du dir über die Vererbungsbeziehung sicher bist, und dynamic_cast wenn nicht. Aber ein komplett anderer Ansatz ohne Downcast wäre nochmals viel besser...



  • wartet mal kurz, ejtzt wirds langsam was komplex.

    downcast ist wenn ich die derive klasse nach base caste, oder?!? (und das geht implizit)

    upcast ist, wenn man aus base ein derive holt (explizit, sprich dynamic/static cast nötig?!?)

    ist denn die methode ein parameterstruct per base und derive struct zu übergeben ok?



  • Skym0sh0 schrieb:

    downcast ist wenn ich die derive klasse nach base caste, oder?!? (und das geht implizit)

    upcast ist, wenn man aus base ein derive holt (explizit, sprich dynamic/static cast nötig?!?)

    Nein, andersrum! Denk Dir einen Vererbungsbaum. Da sind die Basisklassen natürlich oben.

    (Informatiker haben doch den Keller im Speicher und malen Bäume mit der Wurzel oben.)



  • achso 😃 ja logisch eigentlich 😉
    danke



  • Es macht doch keinen Sinn, dass die Basisklasse eine init-Methode hat, die verschiedene Parametertypen erwartet. Dann kann der Aufruf sowieso nicht polymorph sein -> wozu in der Basisklasse halten?

    Und was erzeugt init? Also wo liegen die Objekte nachher? Werden die zurückgegeben? Oder affektiert das die Klasseninstanz selbst? Und kann ich die Klasse nutzen, bevor ich init aufgerufen habe? Falls ja, was ändert init dann? Und wofür ist die Vererbungsbeziehung in erster Linie da? Oder willst Du damit ebenfalls das Initialisierungsproblem lösen?

    Für mich klingt das nach dem init ein wenig nach falschen Verantwortlichkeiten. Entweder, wir können die Klasse sofort nach Instanziierung nutzen oder wir kriegen irgend ein tolles Addon, das wir aber zur Laufzeit dazubsteln wollen. Wenn Du letzteres willst, würde ich das in ein anderes Objekt verlagern und z.B. über Dependency Injection einfügen: (das Wort ist ja gerade "in", aber es wird wohl sehr bald nervig sein so ein Standardkonzept ständig mit nem Buzzword zu versehen :p)

    class Class1
    {
    private:
        Base* base;
    public:
        void setWhatever(Base* base);
    };
    
    class Base
    {
    // ...
    };
    
    class Derived1 : public Base
    {
    public:
        Derived1(const std::string&);
    };
    
    class Derived2 : public Base
    {
    public:
        Derived2(int);
    };
    

Anmelden zum Antworten