views:

345

answers:

7

Suppose we have the following class hierarchy:

class Base {
    ...
};

class Derived1 : public Base {
    ...
};

class Derived2 : public Base {
    ...
};

Given a Base* which could point to either a Derived1 or Derived2 object how can I make a copy of the actual object given that it's concrete type is unknown. I thought of defining copy constructors but I don't think this is possible without knowing the actual types involved. The only solution I can think of is defining a clone() method on each type in the hierarchy. Can anybody think of something more elegant?

+1  A: 

I don't think that it would be easy to do. In fact, in Effective C++, Meyers warned that if you have a function that pass-by-value, and say you have

void foo(Base b)

and you pass a Derived1 d1 into foo, the copy would be sliced off - i.e. the derived parts won't be copied.

But then I'm quoting from memory at the moment...

Calyth
+7  A: 

Unfortunately a virtual clone/copy pattern is your only real choice.

There are variations of this, but essentially they all come down to writing functions for each type that can return a new copy.

Andrew Grant
+4  A: 

You need to implement the virtual clone method on every object in the hierarchy. How else would your object of undefined type know how to copy itself?

In order to make this more obvious to people writing derived classes, you might think about making the clone method abstract in your base class as well, to force people to at least write something.

In several places for my code, I also test to make sure that the to_string() method is implemented correctly to serialize an object to a string. The specific test that I've used in my class to test clone and to_string simultaneously looks like this:

Base *obj1, *obj2;
# initialize obj1 in a reasonable manner
obj2 = obj1->clone();
assert( obj1->to_string() == obj2->to_string() );

This is testing both the clone method() and object serialization to strings (so it's not strictly a unit test), but it's a paradigm that simple to wrap in a loop around all of the objects in a factory to make sure that they're following some minimum standard of implementing clone() and to_string().

Edit: Updated code with correction from strager's comment. Thanks!

James Thompson
I think you mean Base *obj1, *obj2;. =]
strager
Good catch strager!
James Thompson
A: 

A copy constructor is not possible (as far as I know) because memory is allocated before the constructor is called. The copy constructor of the known type is used, not of the type of the object.

Base *foo = new Derived1();
Base bar = *foo;               // Base's copy constructor is called.

Your best bet would be to provide a clone() method, as you suggested.

strager
A: 

An alternative is to create a factory-like Base, assuming you know all your derived classes:

On second thought, this is probably a bad idea!

class Base {
    public:
        static Base *clone(const Base *obj) {
            if((Derived1 *o = dynamic_cast<Derived1 *>(obj)) {
                return new Derived1(*o);
            } else if((Derived2 *o = dynamic_cast<Derived2 *>(obj)) {
                return new Derived2(*o);
            }

            /* TODO When more classes are added, put them here.  (Probably bad design, yes.) */

            return new Base(obj);
        }

        Base *clone() const {
            return clone(this);
        }
};
strager
Hilarious - why post it if it's such a bad idea?
Tom
@Tom, Well, it's /probably/ a bad idea. I'm sure there are some rare cases where it may be used...
strager
A: 

If you want a transparent clone method, look into Implicit Sharing.

strager
A: 

You're correct about clone(). You can't use constructors because they force static binding. You can't return an object by value because that again forces static binding. Thus, you need a virtual function that returns a dynamically allocated object.

Tom