Oto moja próba:
template<class T>
class Child : public T
{
public:
typedef T Parent;
};
template<typename _T>
class has_parent
{
private:
typedef char One;
typedef struct { char array[2]; } Two;
template<typename _C>
static One test(typename _C::Parent *);
template<typename _C>
static Two test(...);
public:
enum { value = (sizeof(test<_T>(nullptr)) == sizeof(One)) };
};
class A
{
public :
virtual void print() = 0;
};
class B : public Child<A>
{
public:
void print() override
{
printf("toto \n");
}
};
template<class T, bool hasParent = has_parent<T>::value>
class ICovariantSharedPtr;
template<class T>
class ICovariantSharedPtr<T, true> : public ICovariantSharedPtr<typename T::Parent>
{
public:
T * get() override = 0;
};
template<class T>
class ICovariantSharedPtr<T, false>
{
public:
virtual T * get() = 0;
};
template<class T>
class CovariantSharedPtr : public ICovariantSharedPtr<T>
{
public:
CovariantSharedPtr(){}
CovariantSharedPtr(std::shared_ptr<T> a_ptr) : m_ptr(std::move(a_ptr)){}
T * get() final
{
return m_ptr.get();
}
private:
std::shared_ptr<T> m_ptr;
};
I trochę przykład:
class UseA
{
public:
virtual ICovariantSharedPtr<A> & GetPtr() = 0;
};
class UseB : public UseA
{
public:
CovariantSharedPtr<B> & GetPtr() final
{
return m_ptrB;
}
private:
CovariantSharedPtr<B> m_ptrB = std::make_shared<B>();
};
int _tmain(int argc, _TCHAR* argv[])
{
UseB b;
UseA & a = b;
a.GetPtr().get()->print();
}
Objaśnienia:
Rozwiązanie to wymaga meta-progamming i modyfikować klasy używane w kowariantna inteligentne kursory .
Prosty szablon struct Child
służy do wiązania typu Parent
i dziedziczenia. Każda klasa dziedzicząca po Child<T>
odziedziczy po T
i zdefiniuje T
jako Parent
. Klasy używane w kowariantnych inteligentnych wskaźnikach wymagają zdefiniowania tego typu.
Klasa has_parent
służy do wykrywania czasu kompilacji, jeśli klasa definiuje typ Parent
lub nie. Ta część nie jest moja, użyłem tego samego kodu, który wykrywa, czy istnieje metoda (see here)
Ponieważ chcemy kowariancji za pomocą inteligentnych wskaźników, chcemy, aby nasze inteligentne wskaźniki naśladowały istniejącą architekturę klas. Łatwiej wyjaśnić, jak to działa w przykładzie.
Po zdefiniowaniu CovariantSharedPtr<B>
dziedziczy po ICovariantSharedPtr<B>
, co jest interpretowane jako ICovariantSharedPtr<B, has_parent<B>::value>
. Ponieważ B
dziedziczy po Child<A>
, has_parent<B>::value
jest prawdziwe, więc ICovariantSharedPtr<B>
jest ICovariantSharedPtr<B, true>
i dziedziczy po ICovariantSharedPtr<B::Parent>
, co oznacza ICovariantSharedPtr<A>
. Ponieważ A
nie ma zdefiniowanego , has_parent<A>::value
jest fałszywe, ICovariantSharedPtr<A>
jest ICovariantSharedPtr<A, false>
i dziedziczy z niczego.
Głównym punktem jest B
dziedziczy z A
mamy ICovariantSharedPtr<B>
dziedziczenie z ICovariantSharedPtr<A>
. Tak więc każda metoda zwracająca wskaźnik lub odniesienie na ICovariantSharedPtr<A>
może zostać przeciążona przez metodę zwracającą to samo na ICovariantSharedPtr<B>
.
JEŚLI nie używasz boost :: shared_ptr, czy zwrócisz wskazówki? Czy zarządza C++? – Lev
@Lev Jeśli próbuję zwracać surowe wskazówki, kod się kompiluje, ale pojawia się problem z zarządzaniem pamięcią. Nie, nie używam zarządzanego C++. –
Co mogę zrobić: Zwróć surowe wskaźniki, ale udokumentuj, że wywołujący jest odpowiedzialny za zawijanie wskaźnika w inteligentnym wskaźniku, np. 'std :: unique_ptr (obj.clone())'. –