3

I have a collection of classes all derived from a common base. I need a collection (probably a list) that will hold instances of derived classes of various types. Class operations will call virtual methods on the instances in the collection. Of course, I can't use list<Base> because holding the base class by value will slice the derived classes.

The obvious solution is to use list<Base*> and wrap it in a class with a copy constructor, a destructor, and so on. The base class would have a virtual duplicate function that is overloaded in each derived class to return a pointer to a copy-constructed new instance of that derived class. The destructor has to traverse the list and delete each item.

If there's a Boost way to do this, that's fine. I'm using Boost already. It seems to me that using Boost's shared pointers would be the best way. There would be overhead in managing the shared reference counts, but that's got to be less expensive than the allocate/copy required in the pointer case. However, this will mean that copies of the collection will result in the same instances and changing one copy will change the other.

I don't yet completely know how this code is going to actually be used. So I'm not sure if the shared copy semantics are going to be a problem. I don't think copies are going to be common, they just need to be handled sanely.

Is there another way?

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • 1
    If objects are not heavy then first try boost pointer containers, else try an STL container (vector, list, etc.) with boost::shared_ptr – bobah Mar 13 '12 at 05:53

1 Answers1

10

Say hello to Boost.PointerContainer. By default, these offer deep-copy semantics when you copy the containers (this is, however, customisable). Note that you need to implement a free new_clone method for your abstract base class in the appropriate namespace.

Also, as a side note, please use std::vector as your first-choice container.

Another side note, don't wrap the container<Base*>, but rather wrap the Base* in, you guessed it, a smart pointer. C++11 offers unique_ptr if you don't want shared ownership. unique_ptr only allows moving it, you can't copy it.

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • I should have guessed that Boost has a class that specifically solved this exact problem! With this solution, I'll need a null object type. I'll probably just use the base class for that. – David Schwartz Mar 13 '12 at 05:48
  • @David: See also [this related question](http://stackoverflow.com/q/9645090/500104) wrt `new_clone`. – Xeo Mar 13 '12 at 05:50
  • @DavidSchwartz: you do *not* need a null object type. A `boost::ptr_vector` is specifically designed not to allow null pointers by default, *but* it can be tuned with `nullable`, see [the tutorial](http://www.boost.org/doc/libs/1_49_0/libs/ptr_container/doc/tutorial.html#null-values) – Matthieu M. Mar 13 '12 at 07:33
  • @MatthieuM.: Thanks! I still think I want a null object type since null objects will be rare and it'd be easier to just call methods on them that know to do the right thing than have to check for null everywhere. But it's nice that I'm doing it because I think it's best, not because I'm forced to do it by the container. – David Schwartz Mar 13 '12 at 07:36
  • @DavidSchwartz: If it is meaningful for your application, then I agree a null object type can make your life easier. – Matthieu M. Mar 13 '12 at 07:42
  • I kind of stopped reading your answer at first after I saw the pointer to Boost.PointerContainer. I shouldn't have. Your last two paragraphs are bang on too. Thanks again. – David Schwartz Mar 13 '12 at 09:49