Как реализовать класс Cloneable template в C++?

80
11

Я пытаюсь реализовать базовый класс Cloneable. Здесь код:

class Base1 {
public:
virtual Base1* Clone() const {
return new Base1(*this);
}
};

class Derived1 : public Base1 {
public:
virtual Derived1* Clone() const {
return new Derived1(*this);
}
};

template<typename T>
class Cloneable {
public:
virtual T* Clone() const {
return new T(dynamic_cast<const T&>(*this));
}
};

class Base2 : public Cloneable<Base2> {
};

class Derived2 : public Base2, public Cloneable<Derived2> {
};

namespace Lib_UnitTest {
TEST_CLASS(CloneableTest) {
public:
// Success test
TEST_METHOD(ShouldClone1) {
Derived1 derived;
Base1& base = derived;
Base1* basePtr = base.Clone();
Derived1* derivedPtr = dynamic_cast<Derived1*>(basePtr);
Assert::IsNotNull(derivedPtr);
}

// Failed test !!!
TEST_METHOD(ShouldClone2) {
Derived2 derived;
Base2& base = derived;
Base2* basePtr = base.Clone();
Derived2* derivedPtr = dynamic_cast<Derived2*>(basePtr);
Assert::IsNotNull(derivedPtr);
}
};
}

Проблема здесь в тестовом случае "ShouldClone1", но "ShouldClone2" потерпел неудачу.

Похоже, что во втором тестовом случае метод Clone не переходит к классу Derived.

Вы знаете, в чем проблема? Какая разница между этими двумя реализациями?

Думаю, это из-за проблемы с наложением алмаза. Но если использовать наследование "public virtual" и реализовать метод Clone в классе Derived2, то мне не нужно наследовать Cloneable для него в первую очередь...

Обновленная версия:

class Cloneable {
public:
// Could make this pure virtual
virtual ~Cloneable() {}

// Could make this pure virtual
virtual Cloneable* Clone() const {
return new Cloneable(*this);
}
};

template<typename T>
class CloneableBase : public virtual Cloneable {
public:
virtual ~CloneableBase() {}

virtual Cloneable* Clone() const {
return new T(dynamic_cast<const T&>(*this));
}
};

class Base2 : public virtual CloneableBase<Base2> {
};

class Derived2 : public virtual Base2 {
virtual Cloneable* Clone() const {
return new Derived2(*this);
}
};

Обновление 2:

Статья Полиморфное клонирование и CRTP описывают решение достаточно ясно.

спросил(а) 2021-01-19T16:10:41+03:00 9 месяцев, 1 неделя назад
1
Решение
80

Это возможно без ручного ввода метода Clone() в каждом производном классе. Но мы не можем избавиться только от CRTP. Вместо непосредственного создания производных классов мы всегда будем опосредованно создавать супер-производный шаблонный класс, который будет решать проблему с алмазами для нас.

class IClonable
{
public: virtual IClonable *
CloneImpl(void) const = 0;

public: virtual
~IClonable(void) {}
};

template< typename TClonable > TClonable *
Construct();

template< typename TBase >
class TheFinalClass final: public TBase, public virtual IClonable
{
template< typename TClonable > friend TClonable *
Construct();

private:
TheFinalClass(void) {}

private: explicit
TheFinalClass(TheFinalClass const & other): TBase(other) {}

private: IClonable *
CloneImpl(void) const override
{
return(new TheFinalClass(*this));
}
};

template< typename TClonable > TClonable *
Construct() {return(new TheFinalClass< TClonable >());}

template< typename TDerived >
class Cloneable: public virtual IClonable
{
public: TDerived *
Clone() const // notice that this method does not override anything
{
return(dynamic_cast< TDerived * >(dynamic_cast< IClonable const * >(this)->CloneImpl()));
}
};

class Base2: public Cloneable< Base2 > {};

class Derived2: public Cloneable< Derived2 >, public Base2 {};

// Failed test !!!, Now works!!!
TEST_METHOD(ShouldClone2) {
Derived2 * p_derived{Construct< Derived2 >()};
Base2 * p_base{p_derived};
Base2 * p_clone_base{p_base->Clone()};
Derived2 * p_clone_derived{dynamic_cast< Derived2 * >(p_clone_base)};
Assert::IsNotNull(p_clone_derived);
}

ответил(а) 2021-01-19T16:10:41+03:00 9 месяцев, 1 неделя назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема