C++
Typ kowariancji zwrotu
Szukaj…
Uwagi
Kowariancja parametru lub wartości zwracanej dla wirtualnej funkcji składowej m
ma miejsce, gdy jej typ T
staje się bardziej szczegółowy w zastąpieniu m
klasę pochodną. Typ T
zmienia się następnie ( wariancja ) pod względem swoistości w ten sam sposób ( co ), co klasy zapewniające m
. C ++ zapewnia obsługę języka dla kowariancyjnych typów zwracanych, które są surowymi wskaźnikami lub surowymi referencjami - kowariancja dotyczy typu pointee lub referencyjnego.
Obsługa C ++ jest ograniczona do typów zwracanych, ponieważ zwracane wartości funkcji są jedynymi czystymi argumentami wyjściowymi w C ++, a kowariancja jest bezpieczna tylko dla czystego argumentu wyjściowego. W przeciwnym razie kod wywołujący mógłby dostarczyć obiekt mniej określonego typu, niż oczekuje kod odbiorczy. Profesor MIT Barbara Liskov zbadała to i powiązane z nią problemy bezpieczeństwa typu wariancji, a obecnie jest ona znana jako Zasada Zastępowania Liskowa lub LSP .
Obsługa kowariancji zasadniczo pomaga uniknąć downcastingu i dynamicznego sprawdzania typu.
Ponieważ inteligentne wskaźniki są typu klasy, nie można użyć wbudowanej obsługi kowariancji bezpośrednio dla wyników inteligentnych wskaźników, ale można zdefiniować pozornie nie- virtual
funkcje opakowania wyników inteligentnych wskaźników dla kowariantnej funkcji virtual
która wytwarza surowe wskaźniki.
1. Przykład podstawowy bez zwrotów kowariantnych pokazuje, dlaczego są one pożądane
// 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. Wersja wynikowa kowariantna przykładu podstawowego, sprawdzanie typu statycznego.
// 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. Wynik inteligentnego wskaźnika kowariantnego (automatyczne czyszczenie).
// 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.
}