如何在C ++中实现Cloneable模板类?

时间:2017-03-30 16:03:09

标签: c++

我正在尝试实现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非常清楚地描述了解决方案。

1 个答案:

答案 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);
}