C++:深度复制基类指针

Posted

技术标签:

【中文标题】C++:深度复制基类指针【英文标题】:C++: Deep copying a Base class pointer 【发布时间】:2012-08-28 15:28:12 【问题描述】:

我四处搜索,似乎为了执行此操作,我需要更改我的 Base 类并想知道这是否是最好的方法。 例如, 我有一个基类:

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) 

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

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

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

那么有没有更简单、更标准的方法呢?

【问题讨论】:

如果你使用的是 POD 数据类型,我会说memcpy,但既然你不是,你可以使用模板。 【参考方案1】:

您需要使用虚拟复制模式:在执行复制的接口中提供一个虚拟函数,然后在整个层次结构中实现它:

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() )
   
;

【讨论】:

谢谢。我们不需要在基类 clone() 中返回 this* 吗? @madu 如果你想拥有基类的实际对象,你应该以与派生类相同的方式实现base::clonereturn new base(*this);。如果您只想将基类用作基类,而不是实例化它的实际对象,最好通过设置virtual base *clone() const = 0; 跳过其定义。 @madu:是的,这是答案中的一个错误。虚成员函数应该是纯的或正确实现的。 你在哪里覆盖clonebase::clone 的返回类型是base * 其中derived::clone 的返回类型是derived *,这不是同一个数据类型,是吗?我认为derived::clone 应该返回base * @MuhamedCicak:它被称为可变返回类型。覆盖函数的返回类型不必相同,必须是协变的【参考方案2】:

使用采用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())
  
;

【讨论】:

为了整洁而加一个。这是我见过的最优雅的方式。 @jogojapan 优雅 +1。如果我们需要从 Derived1 继承 Derived11 怎么办?用 Derived11 模板参数覆盖克隆函数还是有正确的方法?【参考方案3】:

我认为在这种情况下模板是最好的选择:

template<typename Sub>
class DeepCopy

    Base *base;

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

这确实意味着DeepCopy 不能相互分配,但这就是您使用 C++ 所付出的代价。

【讨论】:

以上是关于C++:深度复制基类指针的主要内容,如果未能解决你的问题,请参考以下文章

关于C++基类、派生类的引用和指针

指向派生对象的基类指针的 C++ 排序容器

C++ 在函数外使用基类指针

C++ - 基类指针,方法指针,指派生类,方法?

C++获取基类指针所指子类对象的类名

C++继承,发送一个指向基类的指针