48

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?

4
  • The optimal way to do this might depend on the bigger picture why and in which situation you are trying to do this... Commented Feb 28, 2011 at 23:18
  • I have a complex tree of different objects, some polymorphic. I want to duplicate the tree using a recursive algorithm. Commented Feb 28, 2011 at 23:25
  • Hope this helps: stackoverflow.com/questions/3831370/…. Follow the links in the sentence mentioned as "this, this and this" Commented Mar 1, 2011 at 5:01
  • Clonable pattern has a few important properties you need to be aware of. This article is worth a read: herbsutter.com/2019/10/03/… Commented Feb 8, 2021 at 9:02

3 Answers 3

46

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&) : ... {}
};
Sign up to request clarification or add additional context in comments.

7 Comments

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.
Why use the covariant return type here? Doesn't Base* Derivedn::Clone() { return new Derivedn(*this); } work equally well?
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)
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?
So In have to implement the exactly same method for every subclass? That's just gross :(
|
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.

2 Comments

But it can't be called polymorphically. The parameter is passed by value and will cause slicing in any polymorphic usage.
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<T>(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.
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

*/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.