All, this seems like a simple problem but it's giving me fits. Lets say I have a C++ base class called Animal and derived classes Cat, Dog, Horse, Hippo, Snake. I have another class called Zoo that contains a list (Qt List in this case where the base class Animal is a QObject) of Animal objects. Now what I want to do is create a Zoo object and be able to copy it to another Zoo object and have the destination Zoo have it's own copy of the Animals. The problem is in the Zoo copy constructor because it thinks it has a list of Animals it calls the copy constructor for Animal instead of Dog, Cat, etc. as compilers are ought to do. To get around this I changed the Animal list to be a list of Animal pointers and then created my own Animal copy constructor and assignment operator. Each of these calls the Animal::Clone( Animal &rhs ) method that in turn calls each derived class of Animals Clone() method that returns a new pointer allocated from the heap with a copy of its data. This all works fine but I keep thinking that I'm missing a more elegant solution. So my question is, when you have a container with objects of a type how do we copy the class that contains the container? I hope that makes sense.
-
2Have you tried making the copy constructor for `Animal` virtual? – Daniel Jun 27 '12 at 21:10
-
I agree with Daniel the only way to override a function of the parent class it to have the function be a virtual function otherwise it comes down to code being generated in the compiler. – Travis Pessetto Jun 27 '12 at 21:12
-
The problem comes when Animal inherits from QObject. If I understand it correctly Qt disallows copies of objects that inherit from QObject. Does anybody know if that is correct? – Brad Jun 27 '12 at 21:20
-
You can make copies, but you can't copy the underlying QObject. Making a copy that uses a fresh QObject is fine! – Kuba hasn't forgotten Monica Jun 27 '12 at 21:31
1 Answers
What you did is almost the idiomatic way of doing it in C++. The virtual clone()
method is in fact known as the virtual copy constructor idiom. Typically, you'd declare it as a virtual abstract method of the following signature.
Animal * clone() const = 0;
Note that it doesn't take any rhs
parameter, it simply returns a clone of itself. Given an instance of an animal Animal * myPet
, you can get a duplicate one by saying
Animal * mySecondPet = myPet->clone();
You're forced to implement it in the derived classes if they are to be instantiated.
[Ed:] As you've found out, it is impossible for a container class to hold the items directly and not through pointers, due to the slicing problem inherent in the design of C++.
In C++, your solution is an idiom: it's as elegant as it gets, and everyone who sees it, who knows their C++, should immediately know what you mean. Idioms and certain design patterns are part of the language. You can't call yourself proficient in a language, whether human or programming one, without knowing the idioms.
In many of my projects, I use an interface class (all abstract virtual methods), called Cloneable
, that consists just of this clone method. It's easy then to enforce some base class to be cloneable: just inherit from Cloneable. I wish that QEvent
was Cloneable
that way, for example. As it is, it's impossible to duplicate QEvents
to post them to multiple event queues.
Daniel's suggestion "Have you tried making the copy constructor for Animal virtual?" can't be taken literally. There's no such thing as virtual copy constructors, or any constructors, really, in C++ - for a simple reason: before an object is constructed, its virtual method table is not finalized. Specifically, the code in Animal's constructor will execute before any derived class's constructors get called to swap out the virtual method table pointer to the one in the derived class. So, if you made any virtual method calls in the constructor, they won't go to any class that's derived from your class. That's it.

- 1
- 1

- 95,931
- 16
- 151
- 313
-
That makes sense. And then in the Zoo class implement the copy constructor and assignment operator to loop through the QList calling each Animal's clone() method, right? – Brad Jun 27 '12 at 21:58