views:

283

answers:

4

Hello,

I am working in cross platform C++, and have some classes defined like so: (heavily simplified for this example)

class ExampleBase
{
public:
    ExampleBase( int blah ) : blah_test(blah) { }

    virtual void DoSomething( ) = 0;
private:
    int blah_test;
};

class ExampleImplementer : public ExampleBase
{
public:
    ExampleImplementer( ) : ExampleBase( 5 ) { }

    virtual void DoSomething( ) { /* unique implementation here */ }
};

Originally I had only a single class, which I stored a bunch of instances of in a std::vector and passed around by const reference. But now I need to have a base class (that I want to keep pure virtual) and a number of polymorphic implementing classes.

What is the best way to have a collection of any implementing instances, and still have leak free easy memory management like having a stack allocated std::vector?

  • Obviously I can't have a std::vector< ExampleBase > now, since std::vector requires the class to be non-pure virtual (since it does internal allocation/copying etc.). I don't want users of my code to accidentally create instances of ExampleBase because that is wrong. I also want to steer clear from any possibilities of object slicing or any other nasties.

  • An array of std::auto_ptr would do the job, but then I have to deal with initializing them all, looking for a "free slot", no iterators etc. It seems a bit crazy to do all of this wheel re-inventing.

  • boost::ptr_vector looked promising, however it behaves a little weirdly in that when building on Linux it needs ExampleBase to be non-pure virtual - and I don't understand why... So boost::ptr_vector is out.

This seems to be a simple, and probably really common situation. So what is the best way to do this? I am open to any other std or boost way of doing this: whichever is "best".

+1  A: 

boost::shared_ptr

AraK
+1  A: 

If you can use boost, try pointer containers

http://www.boost.org/doc/libs/1%5F40%5F0/libs/ptr%5Fcontainer/doc/ptr%5Fcontainer.html

Chris Bednarski
+2  A: 

There are a couple of options.

  • Use boost::ptr_vector as your container. It should work even with abstract base classe. Just make sure that your base class includes a virtual destructor.
  • Use multiple containers that directly manage different objects of derived classes and create another one that only uses pointers. This might be cumbersome and also forces you to think about when/if pointers are invalidated. But it also has the advantage of keeping objects of the same type close together.
  • Use a smart pointer (like boost's shared ownership pointer) and store it into your container. C++0x will offer another nice smart pointer with less overhead that's applicable if you don't want to share ownership: unique_ptr. Unfortunately, there's no good emulation of this in C++03.
  • Use some other form of "handle", an object that wraps and manages a polymorphic object through a pointer with value semantics (This would be another smart pointer you could name "clone_ptr").
sellibitze
Great answer too - wish I could mark multiple answers as accepted!
maxpenguin
+6  A: 

boost::ptr_vector is the way to go if you can have Boost. It should work for your scenario - if it does not, then something else is wrong, so please post the error message.

Alternatively, you can go for std::vector< boost::shared_ptr<ExampleBase> >. This is less ideal, because that refcounting will add some overhead, especially when vector is resized, but otherwise a working solution.

If your implementation supports TR1 (latest versions of g++ and MSVC), then you can use std::tr1::shared_ptr instead. This can actually be superior, because STL implementation is free to optimize based on some inner knowledge - for example, in MSVC, std::vector knows that it can use swap instead of copy constructor for std::tr1::shared_ptr, and does just that, avoiding constant refcount changes.

Pavel Minaev