The recommended "copy", commonly called the "clone", is the usual approach. An alternative could be a factory and dispatch using rtti to find the right handler, then call the copy constructor on the derived type.
struct Abc { virtual void who() const = 0; }; struct A : Abc { virtual void who() const { std::cout << "A" << std::endl;} }; template<class T> Abc* clone(Abc* abc) { T* t = dynamic_cast<T*>(abc); if (t == 0) return 0; return new T(*t); } struct B : Abc { virtual void who() const { std::cout << "B" << std::endl;} }; typedef Abc* (*Cloner)(Abc*); std::map<std::string, Cloner> clones; void defineClones() { clones[ typeid (A).name() ] = &clone<A>; clones[ typeid (B).name() ] = &clone<B>; } Abc* clone(Abc* abc) { Abc* ret = 0; const char* typeName = typeid(*abc).name(); if (clones.find(typeName) != clones.end()) { Cloner cloner = clones[typeName]; ret = (*cloner)(abc); } return ret; } void test () { defineClones(); Abc* a = new A; Abc* anotherA = clone(a); anotherA->who(); Abc* b = new B; Abc* anotherB = clone(b); anotherB->who(); }
While the above works, the factual fact that he uses rtti will be enough to convince most to take a normal approach. However, this was the reason for preventing changes in the base class, this may be useful.
Is it effective? The marginal cost of adding a new type is indeed one line. The trick is that it will be easy to forget to add this line with each new class. Or you can see this as a growth potential, that all the cloning code lives in one file, and we donโt need to change the supported hierarchy to process it.
source share