Kann eine geerbte Klasse eine virtuelle Funktion mit einem anderen Rückgabetyp implementieren (ohne eine Vorlage als Rückgabe zu verwenden)?
Kann eine geerbte Klasse eine virtuelle Funktion mit einem anderen Rückgabetyp implementieren (ohne eine Vorlage als Rückgabe zu verwenden)?
In einigen Fällen ist es für eine abgeleitete Klasse zulässig, eine virtuelle Funktion mit einem anderen Rückgabetyp zu überschreiben, solange der Rückgabetyp mit dem ursprünglichen Rückgabetyp kovariant ist . Betrachten Sie beispielsweise Folgendes:
class Base {
public:
virtual ~Base() {}
virtual Base* clone() const = 0;
};
class Derived: public Base {
public:
virtual Derived* clone() const {
return new Derived(*this);
}
};
Hier Base
definiert eine rein virtuelle Funktion aufgerufen , clone
dass die Renditen ein Base *
. In der abgeleiteten Implementierung wird diese virtuelle Funktion mit einem Rückgabetyp von überschrieben Derived *
. Obwohl der Rückgabetyp nicht mit dem in der Basis identisch ist, ist dies absolut sicher, da Sie jederzeit schreiben würden
Base* ptr = /* ... */
Base* clone = ptr->clone();
Der Aufruf von clone()
gibt immer einen Zeiger auf ein Base
Objekt zurück, da Derived*
dieser Zeiger implizit in a konvertierbar ist Base*
und die Operation genau definiert ist , selbst wenn er a zurückgibt .
Im Allgemeinen wird der Rückgabetyp einer Funktion niemals als Teil ihrer Signatur betrachtet. Sie können eine Mitgliedsfunktion mit jedem Rückgabetyp überschreiben, solange der Rückgabetyp kovariant ist.
Base*
durchlong
undDerived*
mitint
(oder umgekehrt, spielt keine Rolle). Es wird nicht funktionieren.Ja. Die Rückgabetypen dürfen unterschiedlich sein, solange sie kovariant sind . Der C ++ - Standard beschreibt es so (§10.3 / 5):
In Fußnote 98 wird darauf hingewiesen, dass "mehrstufige Zeiger auf Klassen oder Verweise auf mehrstufige Zeiger auf Klassen nicht zulässig sind."
Kurz gesagt, wenn
D
es sich um einen Subtyp von handeltB
, muss der Rückgabetyp der Funktion inD
ein Subtyp des Rückgabetyps der Funktion in seinB
. Das häufigste Beispiel ist, wenn die Rückgabetypen selbst aufD
und basierenB
, dies aber nicht sein müssen. Betrachten Sie dies, wo wir zwei getrennte Typhierarchien haben:struct Base { /* ... */ }; struct Derived: public Base { /* ... */ }; struct B { virtual Base* func() { return new Base; } virtual ~B() { } }; struct D: public B { Derived* func() { return new Derived; } }; int main() { B* b = new D; Base* base = b->func(); delete base; delete b; }
Der Grund dafür ist, dass jeder Aufrufer von
func
einenBase
Zeiger erwartet . JederBase
Zeiger reicht aus. Wenn alsoD::func
versprochen wird, immer einenDerived
Zeiger zurückzugeben, erfüllt er immer den von der Vorfahrenklasse festgelegten Vertrag, da jederDerived
Zeiger implizit in einenBase
Zeiger konvertiert werden kann . So erhalten Anrufer immer das, was sie erwarten.In einigen Sprachen können nicht nur die Rückgabetypen variieren, sondern auch die Parametertypen der überschreibenden Funktion. Wenn sie das tun, müssen sie normalerweise kontravariant sein . Das heißt, wenn a
B::f
akzeptiert wirdDerived*
,D::f
darf a akzeptiert werdenBase*
. Nachkommen dürfen lockerer sein, was sie akzeptieren, und strenger in dem, was sie zurückgeben. In C ++ ist keine Kontravarianz vom Parametertyp zulässig. Wenn Sie die Parametertypen ändern, betrachtet C ++ diese als brandneue Funktion, sodass Sie anfangen, zu überladen und sich zu verstecken. Weitere Informationen zu diesem Thema finden Sie unter Kovarianz und Kontravarianz (Informatik) in Wikipedia.quelle
Eine abgeleitete Klassenimplementierung der virtuellen Funktion kann einen kovarianten Rückgabetyp haben .
quelle