2012-09-04 95 views
18

我周围搜索和似乎为了执行此操作我需要更改我的基类,并想知道这是否是最好的方法。 例如, 我有一个基类:C++:深拷贝一个基类指针

class Base {} 

然后长线派生类:

class Derived_1:: public Base {} 
class Derived_2:: public Derived_1{} 
... 
... 
class Derived_n:: public Derived_M{} 

然后我有另一个类:

class DeepCopy 
{ 
    Base * basePtr; 

    public: 
    DeepCopy(DeepCopy & dc) {} 
} 

假设基地类和Derived_x类拷贝构造函数都被正确编码,那么为DeepCopy编写拷贝构造函数的最佳方法是什么?我们如何知道我们要复制的对象的basePtr中的类?

我能想到的唯一方法就是使用RTTI,但是使用一长串dynamic_casts似乎并不正确。此外,它需要DeepCopy了解Base类的继承层次结构。

我看到的另一种方法是here。但它需要Base和Derived类实现一个克隆方法。

那么有没有一个更容易,标准的做法呢?

+0

如果您使用的是POD数据类型,我会说'memcpy',但因为你不是,你可以使用模板。 –

回答

24

您需要使用虚拟副本模式:

struct base { 
    virtual ~base() {}    // Remember to provide a virtual destructor 
    virtual base* clone() const = 0; 
}; 
struct derived : base { 
    virtual derived* clone() const { 
     return new derived(*this); 
    } 
}; 

然后DeepCopy对象只需要调用:在做副本,然后跨层次实现它的界面提供了一个虚拟函数功能:

class DeepCopy 
{ 
    Base * basePtr;  
public: 
    DeepCopy(DeepCopy const & dc)   // This should be `const` 
     : basePtr(dc.basePtr->clone()) 
    {} 
}; 
+0

谢谢。我们不需要在基类clone()中返回这个*吗? – madu

+1

@madu如果你想拥有基类的实际对象,你应该像派生类一样实现'base :: clone':'return new base(* this);'。如果您想仅将基类用作基类,但不想实例化它的实际对象,最好通过设置'virtual base * clone()const = 0;'来跳过它的定义。 – jogojapan

+0

@madu:对,这是答案中的一个错误。虚拟成员函数应该是纯粹的或正确实现的。 –

1

我认为模板是在这种情况下,以最好的一段路要走:

template<typename Sub> 
class DeepCopy 
{ 
    Base *base; 

    DeepCopy(Sub *sub) 
    { 
     base = new Sub(*sub); // use copy constructor 
    } 
} 

这确实意味着DeepCopy是不可分配的,但这是您用C++支付的价格。

19

使用采用clone()函数的方法是一个很好的解决方案。注意使用CRTP (the curiously recurring template pattern)可以为您节省一些工作。你这样做的方式是引入一个中间级别(以下称为BaseCRTP),它是一个模板并实现clone()函数。当您派生实际的类时,将它们用作它们派生自的基础的模板参数。他们将自动获得为他们实现的clone()函数。确保派生类实现一个拷贝构造函数(或者确保默认是你需要的)。

/* Base class includes pure virtual clone function */ 
class Base { 
public: 
    virtual ~Base() {} 
    virtual Base *clone() const = 0; 
}; 

/* Intermediate class that implements CRTP. Use this 
* as a base class for any derived class that you want 
* to have a clone function. 
*/ 
template <typename Derived> 
class BaseCRTP : public Base { 
public: 
    virtual Base *clone() const { 
     return new Derived(static_cast<Derived const&>(*this)); 
    } 
}; 

/* Derive further classes. Each of them must 
* implement a correct copy constructor, because 
* that is used by the clone() function automatically. 
*/ 
class Derived1 : public BaseCRTP<Derived1> { 
    /*... should have an ordinary copy constructor... */ 
}; 

class Derived2 : public BaseCRTP<Derived2> { 
    /*... should have an ordinary copy constructor... */ 
}; 

然后可以明显地实现DeepCopy类以通常的方式:

class DeepCopy 
{ 
    Base *basePtr;  
public: 
    DeepCopy(const DeepCopy &dc) 
    : basePtr(dc.basePtr->clone()) 
    {} 
}; 
+0

不知道crtp,不错的把戏:) –

+1

再加上一个为这个整洁。这是我见过的最优雅的方式。 – namezero

+0

@jogojapan优雅+1。如果我们需要从Derived1继承Derived11,该怎么办?用Derived11模板参数覆盖克隆函数还是有一个正确的方法? – ataman