A classic approach to implementing this cloning technique is to use covariant return types for the Clone()
method. Coupled with modern RAII techniques (e.g. unique_ptr
et. al.) it offers a very flexible and safe combination to manage and clone the objects appropriately.
One of the advantages of the covariant return type is that you are able to obtain a clone an object (a deep copy) and the return type is at the same level in the hierarchy as the argument (i.e. the return is not always to the base class) and no immediate casting is required. In C++, pointers and references support covariance, values do not support covariance.
Using a smart pointer such as unique_ptr
is advised over raw painters to avoid memory leaks. The clone_unique
factory is modelled on the corresponding make_unique
utility from the standard library and returns a unique_ptr
. It contains explicit type checks on the class hierarchy of the argument and target types.
The solution does require use of std::unique_ptr
. If not available with your compiler, boost provides alternatives for these. There are a few other newer C++ language features, but these can be removed if required.
#include <type_traits>
#include <utility>
#include <memory>
class Device {
public:
virtual Device* Clone() const = 0;
};
class Radar : public Device {
public:
virtual Radar* Clone() const override {
// ^^^^^^ covariant return compared to Device::Clone
return new Radar(*this);
}
};
// Clone factory
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());
}
int main()
{
std::unique_ptr<Radar> radar(new Radar());
std::unique_ptr<Device> cloned = clone_unique<Device>(radar);
}
Sample code.
See this related answer for a longer example.