Interfaces



  • Hab grad n kleines verständnisproblem

    also nehmen wir mal an ich hab so ein standard ableitungskonstrukt

    class Base{};
    class Derived1 : public Base{};
    class Derived2 : public Base{};
    

    soweit so gut
    jetzt will ich ein abstraktes interface für die basis zur verfügung stelln
    also änder ich das ganze in

    class IBase{};
    class Base : public IBase{};
    class Derived1 : public Base{};
    class Derived2 : public Base{};
    

    wenn ich jetzt auch noch interfaces zu den abgeleiteten klassen anbieten will
    hab ich ein problem.
    die einzige lösung die mir bis jetzt eingefallen ist:

    class IBase{};
    class IDerived1{};
    class IDerived2{};
    class Base :  public IBase{};
    class Derived1 : public Base, public IDerived1{};
    class Derived2 : public Base, public IDerived1{};
    

    was mir daran nich gefällt is folgendes:
    wenn ich eine IDerived1 instanz habe und eine funktion in IBase aufrufen will
    muss ich diese funktion abstrakt in IDerived1 definieren und in Derived1
    implementieren indem ich einfach die funktion von IBase aufrufe.

    quasi so:

    class IBase{ virtual void func() = 0;};
    class IDerived1{virtual void func_umleitung() = 0; };
    class Base :  public IBase{virtual void func(){//do something}};
    class Derived1 : public Base, public IDerived1{virtual void func_umleitung(){IBase::func()}};
    

    gibts dafür ne bessere lösung?



  • IDerived erbt von IBase

    Aber das ist Java was du da schreibst. Du hast mit Base bereits eine Interface Definition - nimm Base und verzichte auf IBase.

    PS:
    irgendwas passt da nicht. Warum haben Interfaces plötzlich ne implementierung. Wozu genau hast du IBase und Base? Was ist der Unterschied?



  • wenn IDerived von IBase erbt hab ich doch den guten alten diamond of death bzw.
    wär auch inhaltlich nicht richtig beide klassen auf Base zurückzuführen

    ja hab ich auch schon dran gedacht... hat irgendwas von dem implements kram in
    java

    IBase, IDerived1 und IDerived2 sind abstrakt d.h. kein konstruktor, keine
    variablen und viel wichtiger keine abhängigkeiten zu irgendwelchen headern
    oder bibliotheken

    erzeugt werden die interfaces durch eine factory innerhalb einer dynamischen
    bibliothek die zur laufzeit nachgeladen wird

    base, derived1 und derived2 sind die konkreten implementierungen deren header
    ich nicht aus der bibliothek geben will um die benutzung der funktionalität
    nach außen hin einfach zu halten

    vielleicht wärs besser wenn ich IBase einfach weglass und die
    funktionsdefinitionen bei beiden(IDerived1 und IDerived2) reinschreib

    quasi so:

    class IDerived1{};
    class IDerived2{};
    class Base{};
    class Derived1 : public Base, public IDerived1{};
    class Derived2 : public Base, public IDerived2{};
    

    was meint der meister dazu? 😉



  • [quote="Sovok"]

    class IDerived1{};
    class IDerived2{};
    class Base{};
    class Derived1 : public Base, public IDerived1{};
    class Derived2 : public Base, public IDerived2{};
    

    was meint der meister dazu? 😉

    Sobald du von Derived erbst, hast du wieder das selbe Problem. Ich würde deshalb folgendes vorschlagen:

    class IBase {};
    class Base : public IBase {};
    class IDerived : public IBase {}
    class Derived : public IDerived {}
    

    Das Problem dabei ist, du kannst in Derived nicht von Base erben. Es sei denn wir tricksen mit dem pimpl Idiom rum.

    class IBase { virtual void foo()=0; };
    class Base : public IBase { void foo(){} };
    class IDerived : public IBase { virtual void bar()=0; }
    class Derived : public IDerived {
    ConcreteDerived* impl;
    void foo() { impl->foo(); }
    void bar() { impl->bar(); }
    };
    class ConcreteDerived : public Base {
    void foo() { }
    void bar() { }
    };
    

    Wobei ConcreteDerived eben "package private" ist und du es nie nach aussen gibst. Derived ist quasi die fassade um die laufzeit polymorphie zu ermöglichen.

    ConcreteDerived muss nur per forward declaration bekannt gemacht werden - du hast also nicht mehr abhängigkeiten in den public headern.

    Wenn du dann auch ein Derived2 hättest das von Derived erben sollte, würde es dann so aussehen:

    class IDerived2 : public IDerived { virtual void baz()=0; }
    class Derived2 : public IDerived2 {
    ConcreteDerived2* impl;
    void foo() { impl->foo(); }
    void bar() { impl->bar(); }
    void baz() { impl->baz(); }
    };
    class ConcreteDerived2 : public ConcreteDerived {
    void baz() { }
    };
    


  • Shade Of Mine schrieb:

    class IBase { virtual void foo()=0; };
    class Base : public IBase { void foo(){} };
    class IDerived : public IBase { virtual void bar()=0; }
    class Derived : public IDerived {
    ConcreteDerived* impl;
    void foo() { impl->foo(); }
    void bar() { impl->bar(); }
    };
    class ConcreteDerived : public Base {
    void foo() { }
    void bar() { }
    };
    

    interessante idee 👍
    wenn dann also noch Derived ein friend von ConcreteDerived ist könnte ich
    innerhalb von Derived so tun als wäre Base eine Basisklasse von Derived



  • Sovok schrieb:

    wenn dann also noch Derived ein friend von ConcreteDerived ist könnte ich
    innerhalb von Derived so tun als wäre Base eine Basisklasse von Derived

    Ich würde alle methoden an ConcreteDerived forwarden. Das mit dem friend habe ich jetzt nicht verstanden. Aber ja, im eigentlich muss Derived ein friend von ConcreteDerived sein.



  • Mal ganz doof gefragt: lässt sich sovas nicht mit virtual inheritance lösen?

    struct IBase
    {
        virtual void Foo() = 0;
    };
    
    struct IDerived : virtual IBase
    {
        virtual void Bar() = 0;
    };
    
    struct Base : virtual IBase
    {
        virtual void Foo() {}
    };
    
    struct Derived : virtual IDerived, virtual Base
    {
        virtual void Bar() {}
    };
    

    Normalerweise ist virtual inheritance auch das was man will. Dass so wenig virtual inheritance verwendet wird liegt IMO eher daran dass es in den meisten Fällen keinen Unterschied macht - dort wo es einen Unterschied macht ist aber eher virtual inheritance das was man will, und nicht die "normale" Variante.
    Einziger Vorteil von "normal" (mal davon abgesehen dass man ein Keyword weniger tippen muss): man braucht weniger oft einen (vergleichsweise langsamen) dynamic_cast.



  • virtuelle vererbung ist die holzhammer methode für designprobleme.
    idR sollte man sie vermeiden und ich hatte noch nie eine situation wo sie nötig war.

    hier ist virtuelle vererbung tragbar, aber imho ist mein design deutlich besser.



  • hustbaer schrieb:

    Dass so wenig virtual inheritance verwendet wird liegt IMO eher daran dass es in den meisten Fällen keinen Unterschied macht...

    ...und daran das es in vielen Büchern teilweise nicht, oder nur am Rande erwähnt wird.

    cu André



  • asc schrieb:

    ...und daran das es in vielen Büchern teilweise nicht, oder nur am Rande erwähnt wird.

    ihr kennt die fallstricke von virtueller vererbung aber schon, oder?

    virtuelle vererbung ist die logische antwort auf die symptome eines deadly diamond. aber symptome zu bekämpfen ist doof...



  • Shade Of Mine schrieb:

    asc schrieb:

    ...und daran das es in vielen Büchern teilweise nicht, oder nur am Rande erwähnt wird.

    ihr kennt die fallstricke von virtueller vererbung aber schon, oder?

    Wenn du schon so fragst... vermutlich nicht? Magst du mich aufklären?

    virtuelle vererbung ist die logische antwort auf die symptome eines deadly diamond. aber symptome zu bekämpfen ist doof...

    Der "dreaded diamond" ist IMO nur dann schlimm und zu fürchten wenn die Klasse "oben" Daten enthält. Wenn diese klasse ein reines Interface ist sehe ich echt kein Problem, und verstehe auch nicht was man da gegen virtuelle Vererbung haben sollte/könnte/müsste.



  • hustbaer schrieb:

    Shade Of Mine schrieb:

    asc schrieb:

    ...und daran das es in vielen Büchern teilweise nicht, oder nur am Rande erwähnt wird.

    ihr kennt die fallstricke von virtueller vererbung aber schon, oder?

    Wenn du schon so fragst... vermutlich nicht? Magst du mich aufklären?


Anmelden zum Antworten