von 2 abstrakten klassen erben



  • hallo leute

    ich erbe von 2 abstrakten klassen wobei jede der beiden eine memberfunktion mit selben namen und signatur hat.

    class IClass1
    {
       public:
          ...
          virtual void update(void) = 0;
          ...
    };
    
    class IClass2
    {
       public:
          ...
          virtual void update(void) = 0;
          ...
    };
    

    ich erbe nun beide privat:

    class test : private IClass1, private IClass2
    {
       public:
          virtual void update(void) { ... };
    };
    

    das geht natuerlich in die hose. wie mach ich das richtig ?
    ich muss schliesslich IClass1::update und IClass2::update ueberschreiben.

    Meep Meep


  • Mod

    Schreib eine abstrakte Klasse, die lediglich update enthält und erbe IClass1 und IClass2 virtuell von dieser?



  • class asd : private IClass1, private IClass2
    {
    private:
    	void IClass1::update() {...}
    	void IClass2::update() {...}
    };
    


  • @roflo

    geht das nur inline ?

    oder kann ich das auch auf hpp und cpp aufteilen ?

    Meep Meep



  • Keine Ahnung 🙂 Probiers aus.


  • Mod

    roflo schrieb:

    class asd : private IClass1, private IClass2
    {
    private:
    	void IClass1::update() {...}
    	void IClass2::update() {...}
    };
    

    Was soll das denn sein?



  • Arcoth schrieb:

    roflo schrieb:

    class asd : private IClass1, private IClass2
    {
    private:
    	void IClass1::update() {...}
    	void IClass2::update() {...}
    };
    

    Was soll das denn sein?

    ich kannte die schreibweise auch noch nicht. aber VC2015 schluckt es.
    ich weiĂź nur noch nicht wie ich die zwei funktionen in der cpp definiere. derzeit gibt er mir noch

    error LNK2001: Nicht aufgelöstes externes Symbol ""protected: virtual bool __thiscall BaseControl::[IControlPosition]::update(void)" (?update@?QIControlPosition@@BaseControl@@MAE_NXZ)".
    error LNK2001: Nicht aufgelöstes externes Symbol ""protected: virtual bool __thiscall BaseControl::[IControlSize]::update(void)" (?update@?QIControlSize@@BaseControl@@MAE_NXZ)".
    

    aus

    Meep Meep



  • "Schnelle Aktion" -> "Deklaration/Definition" auf void IClass1::update(); funcktioniert schon mal nicht. da kommt VC nicht damit zurecht.
    werd es wohl inline machen muessen, falls nicht noch jemand drauf kommt wie man das teil in einer cpp definiert



  • Der gcc mag diese jedoch Schreibweise leider nicht. Nimm dann einfach zwei weitere klassen die die methoden implementieren und leite die zu einer einzigen ab. Oder änder die methodennamen.



  • Es gibt hier noch die Möglichkeit, das (Objekt-)Adapter-Pattern zu nutzen. Voraussetzung ist dabei, dass der Client von ' test ' in der Lage ist, an das Interface heranzukommen .. wie genau siehe unten:

    Zunächst benötigst Du dazu den Adapter als Template

    template< typename Ifc, typename T, int Nr >
    class AdapterForUpdate : public Ifc
    {
    public:
        explicit AdapterForUpdate( T& target )
            : target_( &target )
        {}
        void update() override  // konkrete Implementierung
        {                       //  Weiterleitung an 'target'
            target_->MyUpdate( Nr ); // Die 'Nr' ist nicht unbedingt nötig
        }
    private:
        T* target_;
    };
    

    Dann die Integration in test :

    class test // nix Ableitung
    {
    public:
        test();
        void MyUpdate( int fromWhichItf );
    
    private:
        AdapterForUpdate< IClass1, test, 1 > adap1_;
        AdapterForUpdate< IClass2, test, 2 > adap2_;
    };
    // Konstruktor sieht so aus:
    test::test()
        : adap1_( *this )
        , adap2_( *this )
    {}
    

    Und anschlieĂźend muss dem Client(s) von test , bzw. IClass1/2 noch das Interface ĂĽbergeben werden. Zum Beispiel mit einer simplen Methode:

    // Publizieren des Interfaces
    IClass1* test::getIfc1() { // IClass2 entsprechend
        return &adap1_; 
    }
    

    hat u.a. den Vorteil, dass innerhalb eines Objekts der Klasse test , die Herkunft des Aufrufs bekannt ist. Damit kann man auch mehrere individuelle Interfaces gleichen Typs an einem Objekt realisieren.

    GruĂź
    Werner



  • leider kann ich den funktionsnamen nicht aendern.
    die klassen sind vorgegeben.

    die abstrakten klassen habe ich zum aendern von objekteigenschaften.
    zum updaten von den eigenschaften brauch ich ein handle das ich, in dem fall, in test habe.
    ich kann natuerlich von den abstrakten klassen jeweils eine relle klasse ableiten, dort eine referenz oder einen pointer auf das handle speichern
    und dann die klassen anstatt via vererbung mittels komposition verwenden.
    gefaellt mir insofern nicht, weil ich nicht nur 2 abstrakte klassen habe und dann ueberall eine referenz auf den selben handle habe.

    deshalb dachte ich mir, das ich das mit privater vererbung mache. das problem halt, die update funktion.
    insofern nutzt mir der adapter auch nichts. trotzdem danke fuer das codebeispiel.
    Arcoth´s vorschlag mit virtueller vererbung geht da auch nicht, weil ich die interface-klassen nicht aendern kann.



  • class IClass1Proxy : public IClass1
    {
    public:
        virtual proxyupdate() = 0;
        void update() { proxyupdate(); }
    };
    
    class Asdf : public IClass1Proxy, public IClass2
    {
    public:
        void proxyupdate() {}
        void update() {}
    };
    


  • ich schreib nochal pseudocode damit es vielleicht ersichtlicher ist.

    class IClass1
    {
       public:
          ...
          virtual void update(void) = 0;
          ...
    };
    
    class IClass2
    {
       public:
          ...
          virtual void update(void) = 0;
          ...
    };
    
    class test : private IClass1, private IClass2
    {
       public:
          ...
          IClass1* position(void);
          IClass2* dimensions(void);
    
       private:
          void *handle;
    };
    
    // benutzung der klasse test
    test t;
    IClass1 *pos = t.position();
    /* ändern von positionswerten */
    pos->update();
    

    zum automatischen andern der position hab ich einen container dem ich einen IClass1 pointer uebergeben kann.
    update wird also nur ueber einen pointer auf ein IClass1 interface aufgerufen.

    mittels komposition wuerde es wie schon geschrieben funktionieren. nur stoert es mich, das ich da dann in jeder klasse eine referenz auf den handle mitfuehern muss, weil die update funktionen den handle brauchen.

    Meep Meep



  • ich machs jetzt so wie roflo es am ende gemeint hat. scheint mir noch die beste loesung zu sein.

    class IClass1
    {
       public:
          virtual auto update(void) noexcept -> void = 0;
    }; 
    
    class RClass1 : public IClass1
    {
       public:
          virtual auto update_class1(void) noexcept -> void = 0;
          virtual auto update(void) noexcept -> void final { update_class1(); }
    };
    
    class IClass2
    {
       public:
          virtual auto update(void) noexcept -> void = 0;
    };
    
    class RClass2 : public IClass2
    {
       public:
          virtual auto update_class2(void) noexcept -> void = 0;
          virtual auto update(void) noexcept -> void final { update_class2(); }
    };
    
    class test : private RClass1, private RClass2
    {
       public:
          auto class1(void) noexcept -> IClass1* { return static_cast<IClass1*>(this); }
          auto class2(void) noexcept -> IClass2* { return static_cast<IClass2*>(this); }
    
       private:
          virtual auto update_class1(void) noexcept -> void { ... }
          virtual auto update_class2(void) noexcept -> void { ... }
    };
    

    danke fuer eure hilfe

    Meep Meep



  • Auf die Gefahr hin das ich jetzt Falsch liege, aber muesste es nicht reichen statt private zu erben protected zu erben?

    class asd : protected IClass1, protected IClass2{
    protected: 
    virtual void update(){};
    }
    

    Oder falls protected nicht geht sollte ja eigentlich einzelne (nicht virtuelle) Zwischenklasse reichen:

    class asd_ : public IClass1, public IClass2 {
    virtual void update(){};
    }
    class asd : private asd_{
    }
    

    Eigentlich sollten die Methoden in beiden Fällen doch sauber überschrieben werden und die ambiguity, falls es die einzige war, aufgelöst sein.

    Oder seh ich da was falsch?


Log in to reply