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.
 }


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow