C++
Rückgabetyp Kovarianz
Suche…
Bemerkungen
Bei der Kovarianz eines Parameters oder eines Rückgabewerts für eine virtuelle Elementfunktion m
wird der Typ T
in einer abgeleiteten Klasse von m
überschrieben. Der Typ T
variiert dann ( Varianz ) in der Spezifität auf dieselbe Weise ( co ) wie die Klassen, die m
bereitstellen. C ++ bietet Sprachunterstützung für kovariante Rückgabetypen, bei denen es sich um Rohzeiger oder Rohreferenzen handelt. Die Kovarianz bezieht sich auf den Pointee- oder Referenztyp.
Die C ++ - Unterstützung ist auf Rückgabetypen beschränkt, da Funktionsrückgabewerte die einzigen reinen Out-Argumente in C ++ sind und Kovarianz nur für ein reines Out-Argument typsicher ist. Andernfalls könnte der aufrufende Code ein Objekt eines weniger spezifischen Typs liefern, als der empfangende Code erwartet. Die MIT-Professorin Barbara Liskov untersuchte diese und verwandte Sicherheitsprobleme des Typs "Abweichungstyp" und ist jetzt als Liskov-Substitutionsprinzip ( LSP) bekannt .
Die Kovarianz-Unterstützung hilft im Wesentlichen, Downcasting und dynamische Typüberprüfung zu vermeiden.
Da Smart-Pointer vom Klassentyp sind, kann die integrierte Unterstützung für Kovarianz nicht direkt für Smart-Pointer-Ergebnisse verwendet werden. Man kann jedoch scheinbar kovariante nicht- virtual
Smart-Pointer-Wrapper-Funktionen für eine kovariante virtual
Funktion definieren, die rohe Pointer erzeugt.
1. Basisbeispiel ohne kovariante Renditen zeigt, warum sie wünschenswert sind
// 1. Base example not using language support for covariance, dynamic type checking.
class Top
{
public:
virtual Top* clone() const = 0;
virtual ~Top() = default; // Necessary for `delete` via Top*.
};
class D : public Top
{
public:
Top* clone() const override
{ return new D( *this ); }
};
class DD : public D
{
private:
int answer_ = 42;
public:
int answer() const
{ return answer_;}
Top* clone() const override
{ return new DD( *this ); }
};
#include <assert.h>
#include <iostream>
#include <typeinfo>
using namespace std;
int main()
{
cout << boolalpha;
DD* p1 = new DD();
Top* p2 = p1->clone();
bool const correct_dynamic_type = (typeid( *p2 ) == typeid( DD ));
cout << correct_dynamic_type << endl; // "true"
assert( correct_dynamic_type ); // This is essentially dynamic type checking. :(
auto p2_most_derived = static_cast<DD*>( p2 );
cout << p2_most_derived->answer() << endl; // "42"
delete p2;
delete p1;
}
2. Covariante Ergebnisversion des Basisbeispiels, statische Typüberprüfung.
// 2. Covariant result version of the base example, static type checking.
class Top
{
public:
virtual Top* clone() const = 0;
virtual ~Top() = default; // Necessary for `delete` via Top*.
};
class D : public Top
{
public:
D* /* ← Covariant return */ clone() const override
{ return new D( *this ); }
};
class DD : public D
{
private:
int answer_ = 42;
public:
int answer() const
{ return answer_;}
DD* /* ← Covariant return */ clone() const override
{ return new DD( *this ); }
};
#include <iostream>
using namespace std;
int main()
{
DD* p1 = new DD();
DD* p2 = p1->clone();
// Correct dynamic type DD for *p2 is guaranteed by the static type checking.
cout << p2->answer() << endl; // "42"
delete p2;
delete p1;
}
3. Covariant Smart Pointer-Ergebnis (automatisierte Bereinigung).
// 3. Covariant smart pointer result (automated cleanup).
#include <memory>
using std::unique_ptr;
template< class Type >
auto up( Type* p ) { return unique_ptr<Type>( p ); }
class Top
{
private:
virtual Top* virtual_clone() const = 0;
public:
unique_ptr<Top> clone() const
{ return up( virtual_clone() ); }
virtual ~Top() = default; // Necessary for `delete` via Top*.
};
class D : public Top
{
private:
D* /* ← Covariant return */ virtual_clone() const override
{ return new D( *this ); }
public:
unique_ptr<D> /* ← Apparent covariant return */ clone() const
{ return up( virtual_clone() ); }
};
class DD : public D
{
private:
int answer_ = 42;
DD* /* ← Covariant return */ virtual_clone() const override
{ return new DD( *this ); }
public:
int answer() const
{ return answer_;}
unique_ptr<DD> /* ← Apparent covariant return */ clone() const
{ return up( virtual_clone() ); }
};
#include <iostream>
using namespace std;
int main()
{
auto p1 = unique_ptr<DD>(new DD());
auto p2 = p1->clone();
// Correct dynamic type DD for *p2 is guaranteed by the static type checking.
cout << p2->answer() << endl; // "42"
// Cleanup is automated via unique_ptr.
}