Kann eine geerbte Klasse eine virtuelle Funktion mit einem anderen Rückgabetyp implementieren (ohne eine Vorlage als Rückgabe zu verwenden)?
Antworten:
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*
durch long
und Derived*
mit int
(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):
Der Rückgabetyp einer überschreibenden Funktion muss entweder mit dem Rückgabetyp der überschriebenen Funktion identisch oder mit den Klassen der Funktionen kovariant sein . Wenn eine Funktion eine Funktion
D::f
überschreibtB::f
, ist der Rückgabetyp der Funktionen kovariant, wenn sie die folgenden Kriterien erfüllen:
- beide sind Zeiger auf Klassen oder Verweise auf Klassen 98)
- Die Klasse im Rückgabetyp von
B::f
ist dieselbe Klasse wie die Klasse im Rückgabetyp vonD::f
oder ist eine eindeutige direkte oder indirekte Basisklasse der Klasse im Rückgabetyp vonD::f
und ist in zugänglichD
- Beide Zeiger oder Referenzen haben dieselbe Lebenslaufqualifikation, und der Klassentyp im Rückgabetyp von
D::f
hat dieselbe Lebenslaufqualifikation wie oder weniger Lebenslaufqualifikation als der Klassentyp im Rückgabetyp vonB::f
.
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 handelt B
, muss der Rückgabetyp der Funktion in D
ein Subtyp des Rückgabetyps der Funktion in sein B
. Das häufigste Beispiel ist, wenn die Rückgabetypen selbst auf D
und basieren B
, 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
einen Base
Zeiger erwartet . Jeder Base
Zeiger reicht aus. Wenn also D::func
versprochen wird, immer einen Derived
Zeiger zurückzugeben, erfüllt er immer den von der Vorfahrenklasse festgelegten Vertrag, da jeder Derived
Zeiger implizit in einen Base
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 wird Derived*
, D::f
darf a akzeptiert werden Base*
. 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.
Eine abgeleitete Klassenimplementierung der virtuellen Funktion kann einen kovarianten Rückgabetyp haben .