webentwicklung-frage-antwort-db.com.de

Rückgabetyp für virtuelle C ++ - Funktionen

Kann eine geerbte Klasse eine virtuelle Funktion mit einem anderen Rückgabetyp implementieren (ohne eine Vorlage als Rückgabe zu verwenden)?

72
Bob

In einigen Fällen ist es zulässig, dass eine abgeleitete Klasse eine virtuelle Funktion mit einem anderen Rückgabetyp überschreibt, sofern der Rückgabetyp kovariant mit dem ursprünglichen Rückgabetyp 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 definiert Base eine rein virtuelle Funktion mit dem Namen clone, die einen _Base *_ zurückgibt. In der abgeleiteten Implementierung wird diese virtuelle Funktion mit dem Rückgabetyp _Derived *_ überschrieben. Obwohl der Rückgabetyp nicht derselbe wie in der Basis 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 dieser Zeiger implizit in einen _Derived*_ und konvertierbar ist, selbst wenn er einen _Base*_ zurückgibt Die Operation ist genau definiert.

Im Allgemeinen wird der Rückgabetyp einer Funktion niemals als Teil ihrer Signatur betrachtet. Sie können eine Elementfunktion mit einem beliebigen Rückgabetyp überschreiben, solange der Rückgabetyp kovariant ist.

76
templatetypedef

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 kovariant mit den Klassen der Funktionen sein. Wenn eine Funktion D::f Eine Funktion B::f Überschreibt, ist der Rückgabetyp der Funktionen kovariant, wenn sie die folgenden Kriterien erfüllen:

  • beide sind Zeiger auf Klassen oder Verweise auf Klassen98)
  • die Klasse im Rückgabetyp von B::f ist die gleiche Klasse wie die Klasse im Rückgabetyp von D::f oder ist eine eindeutige direkte oder indirekte Basisklasse der Klasse im Rückgabetyp von D::f Und ist zugänglich in D
  • beide Zeiger oder Referenzen haben dieselbe CV-Qualifikation und der Klassentyp im Rückgabetyp von D::f hat dieselbe CV-Qualifikation oder weniger als der Klassentyp im Rückgabetyp von B::f.

In Fußnote 98 wird darauf hingewiesen, dass "mehrstufige Verweise auf Klassen oder Verweise auf mehrstufige Verweise auf Klassen nicht zulässig sind".

Kurz gesagt, wenn D ein Subtyp von B ist, muss der Rückgabetyp der Funktion in D ein Subtyp des Rückgabetyps der Funktion in B. Das häufigste Beispiel ist, wenn die Rückgabetypen selbst auf D und B basieren, dies jedoch nicht sein muss. Betrachten wir dies, wo wir zwei separate 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 kann verwendet werden. Wenn also D::func Verspricht, immer einen Zeiger Derived zurückzugeben, erfüllt er immer den von der Vorgängerklasse festgelegten Vertrag, da jeder Zeiger Derived implizit in einen konvertiert werden kann Base Zeiger. So erhalten Anrufer immer das, was sie erwarten.


Zusätzlich zum Variieren des Rückgabetyps können in einigen Sprachen auch die - Parametertypen der überschreibenden Funktion variieren. Wenn sie das tun, müssen sie normalerweise kontravariant sein . Das heißt, wenn B::f Einen Derived* Akzeptiert, kann D::f Einen Base* Akzeptieren. Nachkommen dürfen weniger akzeptieren als Nachkommen und weniger zurückgeben. In C++ ist keine Parametertyp-Abweichung zulässig. Wenn Sie die Parametertypen ändern, betrachtet C++ diese als brandneue Funktion, sodass Sie anfangen, sich zu überladen und zu verstecken. Weitere Informationen zu diesem Thema finden Sie unter Kovarianz und Kontravarianz (Informatik) in Wikipedia.

48
Rob Kennedy

Eine abgeleitete Klassenimplementierung der virtuellen Funktion kann einen Covariant Return Type haben.

2