45

I have base-class Base from which is derived Derived1, Derived2 and Derived3.

I have constructed an instance for one of the the derived classes which I store as Base* a. I now need to make a deep copy of the object which I will store as Base* b.

As far as I know, the normal way of copying a class is to use copy constructors and to overload operator=. However since I don't know whether a is of type Derived1, Derived2 or Derived3, I cannot think of a way of using either the copy constructor or operator=. The only way I can think of to cleanly make this work is to implement something like:

class Base
{
public:
  virtual Base* Clone() = 0;

};

and the implement Clone in in the derived class as in:

class Derivedn : public Base
{
public:
  Base* Clone() 
  {
    Derived1* ret = new Derived1;
    copy all the data members
  }
};

Java tends to use Clone quite a bit is there more of a C++ way of doing this?

doron
  • 27,972
  • 12
  • 65
  • 103
  • The optimal way to do this might depend on the bigger picture why and in which situation you are trying to do this... – sth Feb 28 '11 at 23:18
  • I have a complex tree of different objects, some polymorphic. I want to duplicate the tree using a recursive algorithm. – doron Feb 28 '11 at 23:25
  • Hope this helps: http://stackoverflow.com/questions/3831370/creating-clone-of-an-object-not-working-with-virtual-base-class. Follow the links in the sentence mentioned as "this, this and this" – Nav Mar 01 '11 at 05:01
  • Clonable pattern has a few important properties you need to be aware of. This article is worth a read: https://herbsutter.com/2019/10/03/gotw-ish-solution-the-clonable-pattern/ – Jorge Bellon Feb 08 '21 at 09:02

3 Answers3

45

This is still how we do stuff in C++ for polymorphic classes, but you don't need to do the explicit copy of members if you create a copy constructor (possibly implicit or private) for your objects.

class Base
{
public:
  virtual Base* Clone() = 0;
};

class Derivedn : public Base
{
public:
  //This is OK, its called covariant return type.
  Derivedn* Clone() 
  {
    return new Derivedn(*this);
  }
private:
  Derivedn(const Derivedn&) : ... {}
};
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
  • 3
    Clone() doesn't have to be abstract. If the subclasses shared common data, there could be a copy constructor for the base class to copy the common parts, and a virtual copy constructor to handle copying the variant parts. Either way it's done, every derived class needs its own cloning method. – Suncat2000 Feb 28 '11 at 23:41
  • 1
    Why use the covariant return type here? Doesn't `Base* Derivedn::Clone() { return new Derivedn(*this); }` work equally well? – Robᵩ Mar 01 '11 at 00:19
  • 4
    It does work just as well until you need access to the Derived for some reason - it costs nothing to use covariant return here and may be helpful later. (And I've certainly found it useful when using this kind of pattern) – Michael Anderson Mar 01 '11 at 00:42
  • 3
    This is alot of biolerplaite code. The Clone function has to be written out for every derived class. is there no way to allocate a new instance of a class that will be specified at run-time? – Johannes Gerer Jun 08 '11 at 17:20
  • @JohannesGerer The reason for adding it to each derived class is to explicitly add type information that only that class is guaranteed to have. No other place with only language guarantees know it is a `Derivedn`exception `Derivedn`. Now with C++11 there is likely some easy way to do that with decltype and auto. – Sqeaky Aug 05 '15 at 21:09
  • 3
    So In have to implement the exactly same method for every subclass? That's just gross :( – Tomáš Zato Nov 01 '16 at 12:53
  • @JohannesGerer The answer for your question is here: https://stackoverflow.com/questions/5731217/how-to-copy-create-derived-class-instance-from-a-pointer-to-a-polymorphic-base-c. – kubus May 11 '22 at 19:34
0
template <class T>
Base* Clone (T derivedobj) {
  T* derivedptr = new T(derivedobj);
  Base* baseptr = dynamic_cast<Base*>(derivedptr);
  if(baseptr != NULL) {
    return baseptr;
  }
  // this will be reached if T is not derived from Base
  delete derivedptr;
  throw std::string("Invalid type given to Clone");
}

The only thing this function requires of the derived classes is that their copy constructor is publicly accessible.

Jon Garbe
  • 25
  • 2
  • 1
    But it can't be called polymorphically. The parameter is passed by value and will cause slicing in any polymorphic usage. – Ben Voigt Feb 09 '21 at 23:15
  • There is more wrong with this code. `Clone` is taking its argument _by value_ which means you called its copy constructor already! This function, with its `dynamic_cast` and backing out if it was the wrong type, is equivalent (in the case where it works) to just `Base* y = new(derivedobj);` but here the error (`T` is not derived from `Base`) is found at compile time. This does not solve the OP's problem at all, since you have to know the actual `T` and also downcast the original pointer back to its actual type before using this. – JDługosz Nov 16 '21 at 15:16
0

I have seen some answers using a template function to clone objects. Let me show you how that will not work. Consider the following code:

This is a special case that shows up when objects are being received from a container of Base objects. The function will return a pointer to the Base even when obj is of type Derived. The template only works when it is called by an object that has not undergone any casting.

#include <iostream>
#include <memory>
#include <vector>

class Base{
public:
    virtual void foo(){}
};

class Derived : public Base{};

template<typename T>  std::shared_ptr<T> foo(const T& obj){
    std::cout << "obj is of type: " << typeid(obj).name() << std::endl;
    std::cout << "T is of type: " << typeid(T).name() << std::endl;
    std::cout << std::endl;
    return std::make_shared<T>(obj); // returns Base pointer
}

int main()
{
    std::vector<std::shared_ptr<Base>> vec {std::make_shared<Base>(), std::make_shared<Derived>()};
    for(auto c: vec)
        foo(*c);

    return 0;
}

/* OUTPUT:
obj is of type: 4Base
T is of type: 4Base

obj is of type: 7Derived
T is of type: 4Base

*/
Shwalala
  • 25
  • 5