A virtual cloning pattern is often used to solve such problems. Classic solutions typically use the sharing option types for the clone() method. Another solution introduces a factory type class (using CRTP) between the base and the derived classes. There are even solutions that simply implement this functionality with macros. See the C ++ Frequently Asked Questions , C ++ idioms and a blog about this . Any of these solutions is viable and most appropriate will depend on the context in which they are used and which are intended to be used.
The classical approach, using covariance return types and in combination with more modern RAII methods ( shared_ptr et al.) Offers a very flexible and safe combination. One of the advantages of the covariant return type is that you can get the clone at the same level in the hierarchy as the argument (i.e., the return is not always to the base class).
The solution requires access to shared_ptr and / or unique_ptr . If this is not available with your compiler, boost provides alternatives for them. The clone_shared and clone_unique models are modeled by the corresponding make_shared and make_unique , which form the standard library. They contain explicit type checks of the hierarchy of argument classes 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 added CMoreDerivedClass and CAnotherDerivedClass to expand the hierarchy a bit to better show type checks, etc.
Code example
Niall
source share