我正在尝试实现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);
}
};
}
这里的问题是测试案例&#39; ShouldClone1&#39;成功,但&#39; ShouldClone2&#39;失败。
似乎在第二个测试用例中,Clone方法没有进入Derived类。
你知道这个问题是什么吗?这两种实现之间的区别是什么?
我想这是因为&#39;钻石&#39;继承问题。但如果使用公共虚拟&#39;继承并实现Derived2类中的Clone方法,然后我不需要在第一时间为它继承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:
文章Polymorphic cloning and the CRTP非常清楚地描述了解决方案。
答案 0 :(得分:0)
无需在每个派生类中手动编写Clone()方法。但是我们无法逃脱CRTP。我们将不会直接实例化Derived类,而是间接实例化超级派生的模板类,它将为我们处理钻石问题。
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);
}