wieder spaß mit vererbung :-)



  • hallo,
    also ich hab mal wieder ein problem mit der vererbung und virtuellen funktionen.
    folgender abstrahierter code:

    class A
    {
    	public:
    		virtual A &foo() = 0;
    };
    
    class B: virtual public A
    {
    	public:
    //		B &foo(){A::foo();/*....*/};
    		B &foo(int para){A::foo();/*....*/ return *this;};
    };
    
    class D: virtual public A
    {
    	public:
    		D &foo(){/*....*/return *this;};
    };
    
    class C: virtual public B, virtual private D
    {
    	public:
    		C &foo(){/*....*/return *this;};
    };
    
    void foo2(B &b)
    {
    	b.foo();// Fehler
    };
    
    int main()
    {
    	C c;
    	c.foo();
    	foo2(c);
    	return 0;
    };
    

    folgende fehlermeldung

    error C2660: 'B::foo': Funktion akzeptiert keine 0 Argumente
    

    folgendes wenn ich das auskommentiert wieder einkommentiere

    error C2250: "C": Mehrdeutige Vererbung von "B &A::foo(void)"
    

    letzters denke ich ist mir klar, liegt daran dass ich in C von B und D erbe. die andere fehlermeldung ist mir nicht so ganz klar, da die methode foo() ja eigentlich in C definiert wird und ich bei der Funktion foo2() ja auch eine Referenz übergebe, er also schon weiß das ein C Objekt dahintersteckt.
    ich könnte mir vorstellen, dass es daran liegt das A und B abstrakt sind, allerdings kann ja bei foo2() nur ein Objekt übergeben werden, das die funktion foo() auch definiert hat, womit es dann eigentlich wiederum kein problem wäre.
    ich bräuchte also mal wieder rat.
    danke schonmal im voraus.

    PS: wen der hintergrund der vererbungsstruktur interessiert:
    A = container (abstrakt)
    B = priority-queue (abstrakt)
    D = array
    C = d-är heap (d=2 binär, d=3 tertiär, ...)
    eigentlich sind das alles noch templates, aber das hab ich jetzt mal weggelassen, da es nichts wesentliches beiträgt, denke ich zumindest.



  • Du überschreibst in C ja keine Methode. Das B::foo nimmt ja einen Parameter und noch dazu hat es einen anderen Rückgabetyp.
    Ergo hast du eine neue Methode, die du mit einem B nicht aufrufen kannst. (Bin nur kurz drübergegangen, also keine Gewähr auf Richtigkeit).



  • Hi,

    im ersten Fall überdeckt der Name B::foo A::foo und somit gibts in diesen Scoop nur foo(int). Überladen funktioniert nicht über Klassen hinweg, nur im selben Scoop.

    Im zweiten auskommentierten Fall sehe ich keinen Fehler, poste mal bitte genau den Code, der diesen Fehler verursacht.

    Desweiteren kannst du dir die virtuals bei class C sparen 😉



  • der fehler kommt am ende der deklaration von klasse C, da sagt er mir dann das C mehrdeutig ist.

    class C: virtual public B, virtual private D
    {
        public:
            C &foo(){/*....*/return *this;};
    }; // hier sagt er mir dann "C": Mehrdeutige Vererbung von "B &A::foo(void)"
    

    bezüglich des rückgabetyps hab ich gelesen, dass kohärente(abgeleitete) typen auch erlaubt sind, eben wie in meinem beispiel.
    A gibt ein A, B ein B, C ein C und D ein D zurück, sie alle beziehen sich aber auf die in A abstrakt definierte methode virtuell A &foo() = 0. so ist es zumindest konzipiert.



  • wenn der name A::foo in B durch B::foo überdeckt wird, hab ich in mal mit using wieder bekannt gemacht. allerdings kennt er jetzt die B:foo(int) nicht mehr.

    //...
    class B: virtual public A
    {
    	public:
    		using A::foo;
    		B &foo(int para){A::foo();/*....*/ return *this;};
    };
    //...
    int main()
    {
    	C c;
    	c.foo(5); //Fehler
    	foo2(c);
    	return 0;
    };
    

    Fehler:

    error C2660: 'C::foo': Funktion akzeptiert keine 1 Argumente
    


  • wie auch. C::foo() überdeckt wieder die geerbten Funktionen.
    Lösung: using B::foo(int);



  • C::foo() hat ja auch keine Parameter, demenstsprechend spuckt der Compiler dir diesen Fehler aus.

    Siehe:

    class C: virtual public B, virtual private D
    {
        public:
            C &foo(){/*....*/return *this;}; // <- kain Parameter
    };
    

    Gruß Kimmi



  • using B::foo(int);
    

    kann schon mal nicht gehen, und geht auch nicht, weil bei using nur die bekanntmachung von namen erlaubt ist, dh. nix mit parameterlisten, man macht damit immer alle methoden mit diesen namen bekannt. einzig zulässig wäre in C

    using B::foo;
    

    dann hab ich aber das problem, dass er jetzt A::foo() in der methode B::foo(int) nicht kennt

    public: virtual class A & __thiscall A::foo(void)" (?foo@A@@UAEAAV1@XZ)" in
    Funktion ""public: class B & __thiscall B::foo(int)" (?foo@B@@QAEAAV1@H@Z)"
    

    also abstrakte methode A::foo() wird ja meines wissens auch erst in C gebunden (späte bindung heißt das wohl), dennoch müsste B sie trotzdem kennen oder zumindest wissen das es eine solche in jeder instanz abgeleiteter nicht abstrakter klassen gibt.



  • also mir ist noch nicht ganz klar warum ich die virtuals bei C weglassen kann, da ich dann zweimal Basisklasse A habe.
    allerdings kann ich bei B und D die virtuals weglassen, wobei er mit dann sagt, dass der rückgabetyp von foo in der klass C nicht mehr kovariant ist, das verstehe ich allerdings nicht, da B und D doch beide von A abgeleitet sind.
    ich bin da jetzt leider etwas verwirrt und komme an diesem punkt nicht weiter.



  • Dann shcau dir am Besten nochmal den Abschnitt zur virtuellen Vererbung im buch deiner Wahl an. Die Klassen, die von der gemeinsamen Basisklasse erben (also in deinem Fall B und D) müssen virtuell von der Basis erben. Die Klasse, die wieder alle Eigenschaften in sich vereint (C) muss nicht virtuell erben.



  • ahh sorry, stimmt natürlich, hab ich verwechselt. wenn ich allerdings C für weitere vererbungen vorsehe, sollten die virtuals bestehen bleiben oder?
    naja, das ändert allerdings nichts am bestehenden problem. using hilft wie gesagt nicht.


  • Administrator

    A::foo()
    

    Ruft explizit die Function foo von A auf, da wird also keine Polymorphie oder sowas angewendet. Nun ist die Funktion foo in A abstrakt, also hat sie keine definition, wodurch der Linker meckert. Wenn du die überschriebene Funktion aufrufen willst, dann musst du foo unqualifiziert aufrufen.

    Und wegen der Mehrdeutigkeit:
    Das scheint ein Problem mit dem Rückgabewert zu sein. Der Kompiler erkennt nicht, dass C& foo() die gleiche Funktion überschreibt wie B& foo() und D& foo() . Wenn du in den Klasse B und D den Rückgabewert auf A& änderst, dann funktioniert es. Keine Ahnung wie der Standard dazu genau lautet und wo jetzt das Problem liegt. Also ob es der Standard so definiert oder der Kompiler hier einen Fehler macht.

    Grüssli



  • also das mit der mehrdeutigkeit hat sich erledigt, das kommt ja nur wenn ich das mit using machen würde.
    wenn ich jetzt aber in B statt A::foo nur foo schreibe, meckert er halt rum das foo keine 0 Parameter akzeptiert, obwohl es ja noch die abstrakte methode von A gibt, die natürlich erst in späteren abgeleiteten klassen definiert wird (das ist ja der sinn der abstrakten methoden).


  • Administrator

    Der folgende Code kompiliert bei mir einwandfrei unter VC2008.

    class A 
    { 
    public: 
    	virtual A &foo() = 0; 
    }; 
    
    class B: virtual public A 
    { 
    public: 
    	A &foo(){foo();/*....*/ return *this; };         // <- einmal A& statt B&
    	B &foo(int para){foo();/*....*/ return *this;}; 
    };
    
    class D: virtual public A 
    { 
    public: 
    	A &foo(){/*....*/return *this;};                 // <- einmal A& statt D&
    }; 
    
    class C: public B, private D 
    { 
    public: 
    	C &foo(){/*....*/return *this;}; 
    }; 
    
    void foo2(B &b) 
    { 
    	b.foo(); 
    }; 
    
    int main() 
    { 
    	C c; 
    	c.foo(); 
    	foo2(c); 
    
    	return 0; 
    };
    

    Was ist bei deinem noch anders, so dass er nicht kompiliert?

    Grüssli



  • das kompiliert bei mir auch, nur das halt bei B und D die rückgabetypen nicht stimmen. die sollten eigentlich schon B bzw D objekte sein, damit ich auch mit den methoden von B und D arbeiten kann und nicht nur mit denen von A. und wegen der kovarianz sollte das auch möglich sein, aber offensichtlich hakt es ja doch irgendwo. und ich würde halt gern verstehen wollen wo.



  • ok, problem gelöst.
    danke für die zahlreichen lösungsvorschläge.


  • Administrator

    FreakyBKA schrieb:

    ok, problem gelöst.
    danke für die zahlreichen lösungsvorschläge.

    Würdest du uns auch verraten wie? Wollte mich gerade durch den Standard wühlen, um herauszufinden, was der Standard dazu genau sagt.
    Oder hast du dein Problem anders gelöst?

    Grüssli



  • ist glaub ich die kombination einiger lösungsvorschläge, und hat wahrscheinlich nur wegen meinem unverständnis so lange gedauert 😉

    class A
    {
    	public:
    		void a(){};
    		virtual A &foo() = 0;
    };
    
    class B: virtual public A
    {
    	public:
    		void b(){};
    //hier stand ja mal A::foo drin, was wegen dem direkten aufruf falsch ist
    		virtual B &foo(int para) {foo(); b(); return *this;};
    //hier wird A::foo wieder sichtbar gemacht
    		using A::foo;
    };
    
    class D: virtual public A
    {
    	public:
    		void d(){};
    		D &foo() {d(); return *this;};
    };
    
    class C: virtual public B, virtual public D
    {
    	public:
    		void c(){};
    		C& foo(){d(); return *this;};
    //dieses using wurde auch mal vorgeschlagen nur als using B::foo(int) oder so
    //damit kann man jetzt auch bei C foo(int) aufrufen
    		using B::foo;
    };
    
    void foo2(B &b)
    {
    	b.b();
    	b.foo();
    	b.foo(5);
    };
    
    int main()
    {
    	C c;
    	c.foo();
    	c.foo(5);
    	foo2(c);
    	return 0;
    };
    

Anmelden zum Antworten