27

I searched around and seems in order to perform this I need to change my Base class and want to know if this is the best approach. For example, I have a Base class:

class Base {}

Then a long line of derived classes:

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

And then I have another class:

class DeepCopy 
{ 
  Base * basePtr;

  public:
   DeepCopy(DeepCopy & dc) {}
}

Assuming the Base class and Derived_x class copy constructors are properly coded, what is the best way to write the copy constructor for DeepCopy. How can we know about the class that is in the basePtr of the object we are going to copy?

Only way I can think of is using RTTI, but using a long list of dynamic_casts seems not right. Besides it requires DeepCopy to know about the inheritance hierarchy of Base class.

The other method I saw is here. But it requires Base and Derived classes implement a clone method.

So is there a much easier, standard way of doing this?

Community
  • 1
  • 1
madu
  • 5,232
  • 14
  • 56
  • 96

3 Answers3

34

You need to use the virtual copy pattern: provide a virtual function in the interface that does the copy and then implement it across the hierarchy:

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

Then the DeepCopy object just needs to call that function:

class DeepCopy 
{ 
  Base * basePtr;    
public:
   DeepCopy(DeepCopy const & dc)           // This should be `const`
      : basePtr( dc.basePtr->clone() )
   {}
};
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Thank you. Do we not need to return this* in Base class clone()? – madu Sep 04 '12 at 01:37
  • 1
    @madu If you want to have actual objects of the base class, you should implement `base::clone` in the same way as for the derived class: `return new base(*this);`. If you want to use the base class only as a base class, but not to instantiate actual objects of it, best skip its definition by setting `virtual base *clone() const = 0;`. – jogojapan Sep 04 '12 at 01:47
  • @madu: Right, that was a bug in the answer. The virtual member function should be either pure or correctly implemented. – David Rodríguez - dribeas Sep 04 '12 at 20:46
  • Where do you override `clone`? The return type of `base::clone` is `base *` where the return type of `derived::clone` is `derived *`, that is not a same data type, is it? I think `derived::clone` should return `base *`. – Silidrone Nov 28 '17 at 14:00
  • 1
    @MuhamedCicak: It's called variadic return type. The return type of the overrider function need not be the same, it needs to be co-variant – David Rodríguez - dribeas Dec 01 '17 at 16:22
  • @DavidRodríguez-dribeas I see. Thanks. – Silidrone Dec 08 '17 at 11:16
  • This answer would benefit from being updated to use smart pointers. – François Andrieux Oct 27 '21 at 01:49
24

Using an approach that employs a clone() function is a good solution. Note using the CRTP (the curiously recurring template pattern) can save you some of the work. The way you do it is by introducing an intermediate level (called BaseCRTP below) which is a template and implements the clone() function. When you derive your actual classes, use them as the template argument of the base they are derived from. They will get the clone() function implemented for them automatically. Make sure the derived classes implement a copy constructor (or be sure the default is what you need).

/* 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... */
};

You can then obviously implement the DeepCopy class in the usual way:

class DeepCopy 
{ 
  Base *basePtr;    
public:
  DeepCopy(const DeepCopy &dc)
    : basePtr(dc.basePtr->clone())
  {}
};
jogojapan
  • 68,383
  • 11
  • 101
  • 131
  • 1
    Plus one for the neatness of this. This is the most elegant way I've ever seen this done. – namezero Jun 02 '15 at 18:53
  • 1
    @jogojapan Elegant +1. What if we need to inherit Derived11 from Derived1? Overwrite clone function with Derived11 template argument or is there a proper way? – ataman Jan 17 '17 at 07:13
1

I think that templates are the best way to go in this situation:

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

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

This does mean that DeepCopy's are un-assignable to each other, but that's the price you pay with C++.

Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201