2017-10-05 2 views
2

Ich frage mich, wie kann ich die Funktion der abgeleiteten Klasse mit einem abgeleiteten Funktionsparameter korrekt überschreiben?
z.B.Vererbung von Funktionsparameter

struct X; 
struct Y:X 
struct Z:X 

class A { 
    public: 
     int f(int, X);  
}; 

class B : A{ 
    public: 
     int f(int, Y); 
}; 

class C : A{ 
    public: 
     int f(int, Z); 
}; 
+0

Wenn es möglich wäre, erwäge, ein X an ein A zu übergeben, dessen dynamischer Typ B ist. Worauf sollte der Aufruf lauten? –

+0

Sie können nicht, wie es falsch wäre.Contravarianz wird nicht unterstützt, nur Kovarianz für Rückgabetyp wird unterstützt. – Jarod42

+0

@ Jarod42 aber nur Kovarianz bei der Rückgabe von Referenz-und Zeiger-und Basis abgeleiteten Beziehungen, die eine Subst Covarianz ist. – Yakk

Antwort

2

Die Idee, dass Sie/Basisklassen für Rückgabewerte abgeleitet substitude können/als kovarianten Rückgabetypen und kontra Argumente Parameter bekannt.

In C++ haben Referenz- und Zeigerrückgabetypen von virtual Zeigern in abgeleiteten Typen eine kovariante Überladung; Sie können einen eingeschränkteren (Zeiger oder Verweis) auf den Basisrückgabetyp in abgeleiteten Typen zurückgeben. Die Contravarianz von Argumenten, bei der Sie ein Derived* Argument in der Basisschnittstelle durch ein Base* Argument ersetzen, wird in C++ nicht unterstützt, aber Sie können es sowohl mit Überladung als auch mit Überschreibung emulieren.

struct BaseValue {}; 
struct DerivedValue:BaseValue {}; 
struct MoreDerivedValue:DerivedValue {}; 

struct BaseInterface { 
    virtual void contra_example(DerivedValue* ptr) = 0; 
    virtual DerivedValue* co_example() = 0; 
    virtual ~BaseInterface() {} 
}; 

struct DerivedInterface:BaseInterface { 
    virtual void contra_example(DerivedValue* ptr) override final { 
    contra_example(static_cast<Value*>(ptr)); 
    } 
    void contra_example(Value* ptr) override = 0; 

    virtual MoreDerivedValue* co_example() override = 0; 
}; 

co_example ist ein Beispiel für die Kovarianz in den Rückgabetyp. Der Compiler macht dies automatisch für uns, wenn wir nur eine Kovarianz machen, die auf Zeigern und Referenzen in einer Typhierarchie basiert.

contra_example ist ein Beispiel für die Kontravarianz des Argumenttyps. ptr, das Argument zu contra_example, im Fall von DerivedInterface kann jede Value* sein. Die Basisschnittstelle erfordert, dass es ein DerivedValue* ist.

Wir überschreiben die Basis contra_example, dann weiter zu unserer internen „mehr akzeptieren“ Implementierung, die eine Überlastung in DerivedInterface.

Die abgeleitete Schnittstelle ist zügige als die Basisschnittstelle, und es bietet Garantien mindestens so gut oder besser ist als die ursprüngliche tut.


Jetzt lassen Sie uns zurück auf Ihre Frage. Erstens, nein, der Compiler wird es nicht für Sie tun.

Zweitens ist Ihre Logik fehlerhaft. Unter Verwendung der Liskov substitution principle muss Ihre B Ersatz für eine A sein.

Aber Ihre B hat einen mehr eingeschränkt Vertrag auf ihrem Argument zu f als A tut. A erfordert ein X Argument, B erfordert, dass es nicht nur ein X sondern auch ein Y ist.

struct X1{}; 
struct X2{}; 
struct Y:X1,X2{}; 

struct A { 
    virtual void foo(Y*) = 0; 
    virtual ~A() {} 
} 
struct B1:A { 
    virtual void foo(Y* ptr) final override { foo(static_cast<X1*>(ptr)); } 
    virtual void foo(X1*) = 0; 
} 
struct B2:A { 
    virtual void foo(Y* ptr) final override { foo(static_cast<X2*>(ptr)); } 
    virtual void foo(X2*) = 0; 
} 

Dies entspricht Ihrem Beispiel. Die abgeleiteten Interfaces sind toleranter für ihr (implizit eingegebenes) Argument.

Sie können durch die Rahmen springen, um Argumente zu erhalten, die die Kovarianz unterstützen oder einfach zurückgeben.