Published by
Jan 27, 2010

The clone pattern

Score: 4.5/5 (21 votes)
*****
In order to copy an object, you have to know at compile time the
object's type, because the type is the "name" of the copy constructor:

1
2
3
4
void copy_me( const std::string& s ) {     
    std::string  s_copy( s );
    std::string* p_s_copy = new std::string( s );
}


I know at compile time that s has type "std::string" because it says
so in the parameter list. But what if type of s was a base class?

1
2
3
4
5
6
class Base {};
class Derived : public Base {};

void copy_me( const Base& b ) {
    Base b_copy( b );   // ????
}


That doesn't quite work, because I can call copy_me() with a derived
class instance, in which case the function would want to instantiate
a Derived object, not a Base object. But at compile time there is
simply no way for me to know that. Indeed, I could even call copy_me()
with Base instances in one place, Derived in another, and something
else (derived from Base or Derived) in a third.

How can this problem be solved?

The clone pattern was implemented for exactly this reason. The
clone pattern looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Depending upon your needs, you might not require a base class
// clonable concept.  It would only be needed if you need to store
// clonable objects polymorphically.
struct clonable {
    virtual ~clonable() {}
    virtual clonable* clone() const = 0;
};

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

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


And now, copy_me looks like:

1
2
3
4
void copy_me( const Base& b ) {
    Base* clone = b.clone();
    // delete clone;  
};


And I've successfully invoked Base's copy constructor if the
"real type" of b is Base, and Derived's copy constructor if the
"real type" of b is Derived.

It is worth mentioning here that this technique exploits the fact that
the compiler does not consider the return type of the function when
determining whether or not a derived class virtual method has overridden
a base class one with the same name.