Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to clone as derived object in C++

Tags:

c++

clone

I define two classes in C++. Ones is the base class, and one is a derived class

    class CBaseClass
    {
    …
    }

    class CDerivedClass : public CBaseClass
    {
    …
    }

And want to implement a clone function as follows:

    CBaseClass *Clone(const CBaseClass *pObject)
    {
    }

When an object of CDerivedClass is passed to Clone, then the function will also create a CDerivedClass object and return. When an object of CBaseClass is passed to Clone, then the function will also create a CBaseClass object and return.

How to implement such a feature?

like image 408
alancc Avatar asked Jul 24 '14 16:07

alancc


2 Answers

The virtual clone pattern is often used to solve problems such as these. Classic solutions tend to use co-variant return types for the clone() method. Other solution inject a factory type class (using CRTP) between the base and the derived classes. There are even solutions that just implement this functionality with macros. See the C++ FAQ, C++ idioms and a blog on this. Any one of these solutions is viable and the most appropriate would depend on the context in which they are used and intended to be used.

A classic approach, using covariant return types and coupled with more modern RAII techniques (shared_ptr et. al.) offers a very flexible and safe combination. One of the advantages of the covariant return type is that you are able to obtain a clone at the same level in the hierarchy as the argument (i.e. the return is not always to the base class).

The solution does require access to shared_ptr and/or unique_ptr. If not available with your compiler, boost provides alternatives for these. The clone_shared and clone_unique are modelled on the corresponding make_shared and make_unique utilities form the standard library. They contain explicit type checks on the class hierarchy of the arguments and target types.

#include <type_traits>
#include <utility>
#include <memory>

class CBaseClass {
public:
  virtual CBaseClass * clone() const {
    return new CBaseClass(*this);
  }
};

class CDerivedClass : public CBaseClass {
public:
  virtual CDerivedClass * clone() const {
    return new CDerivedClass(*this);
  }
};

class CMoreDerivedClass : public CDerivedClass {
public:
  virtual CMoreDerivedClass * clone() const {
    return new CMoreDerivedClass(*this);
  }
};

class CAnotherDerivedClass : public CBaseClass {
public:
  virtual CAnotherDerivedClass * clone() const {
    return new CAnotherDerivedClass(*this);
  }
};

// Clone factories

template <typename Class, typename T>
std::unique_ptr<Class> clone_unique(T&& source)
{
  static_assert(std::is_base_of<Class, typename std::decay<decltype(*source)>::type>::value,
    "can only clone for pointers to the target type (or base thereof)");
  return std::unique_ptr<Class>(source->clone());
}

template <typename Class, typename T>
std::shared_ptr<Class> clone_shared(T&& source)
{
  static_assert(std::is_base_of<Class, typename std::decay<decltype(*source)>::type>::value,
    "can only clone for pointers to the target type (or base thereof)");
  return std::shared_ptr<Class>(source->clone());
}

int main()
{
  std::unique_ptr<CDerivedClass> mdc(new CMoreDerivedClass()); // = std::make_unique<CMoreDerivedClass>();
  std::shared_ptr<CDerivedClass> cloned1 = clone_shared<CDerivedClass>(mdc);
  std::unique_ptr<CBaseClass> cloned2 = clone_unique<CBaseClass>(mdc);
  const std::unique_ptr<CBaseClass> cloned3 = clone_unique<CBaseClass>(mdc);
  // these all generate compiler errors
  //std::unique_ptr<CAnotherDerivedClass> cloned4 = clone_unique<CAnotherDerivedClass>(mdc);
  //std::unique_ptr<CDerivedClass> cloned5 = clone_unique<CBaseClass>(mdc);
  //auto cloned6 = clone_unique<CMoreDerivedClass>(mdc);
}

I've added a CMoreDerivedClass and CAnotherDerivedClass to expand the hierarchy a little to better show types checks etc.

Sample code

like image 91
Niall Avatar answered Oct 20 '22 12:10

Niall


Here is a simple solution. Remember to provide a Clone for each class in the inheritance.

class Base
{
public:
    virtual ~Base() {}
    virtual Base *Clone() const
    {
        // code to copy stuff here
        return new Base(*this);
    }
};

class Derived : public Base
{
public:
    virtual Derived *Clone() const
    {
        // code to copy stuff here
        return new Derived(*this);
    }
};
like image 35
Neil Kirk Avatar answered Oct 20 '22 12:10

Neil Kirk