Pryo
Pryo

Reputation: 690

C++, Boost: How to store objects of an abstract type in a map container

I am trying to use a map container to hold Shapes and match those shapes to an ID number.

Until now, I have always used STL containers to hold and memory-manage my objects. So I would use containers of these sorts:

std::map<int, Square> squares;
std::map<int, Triangle> triangles;
std::map<int, Circle> circles;

But I want to have a single map to hold "Shapes", and this is an abstract base class of Square, Triangle and Circle. So to achieve this, I would still store the realisable derived class objects in their own maps, and then have another map:

std::map<int, Shape*> shapes;

to store pointers to the objects stored in the other maps.

This seems very messy though, and I'd rather store all the objects in a single polymorphic map that owns and memory-manages the contained objects.

After reading a little about Boost's ptr_map I expected this was the solution. But it seems that the base class needs to be realisable as while trying to use:

boost::ptr_map<int,Shape> shapes;

I get the error: "error: cannot allocate an object of abstract type 'Shape'"

Must I make the base class realisable? That would be a bit of a hack so I'd rather do this properly, if there is such a way.

My next best guess about how to do this is to use a container such as:

std::map<int, boost::shared_ptr<Shape> > shapes;

This seems like such a straightforward aim, but since I'm having such difficulty with it I suspect I'm trying to do something I shouldn't. So any advice about where I might be going wrong is greatly appreciated.

Thanks.

Upvotes: 2

Views: 1166

Answers (2)

Luc Touraille
Luc Touraille

Reputation: 82071

ptr_map<int, Shape> seems like the way to go, and works even with abstract base type (see an example here). I guess the error you obtain comes from the use of operator[]. Indeed, like in std::map, the operator[] returns a default-constructed value if the key was not already in the map. In this case, it can't construct the value since Shape is abstract, hence the compiler error.

So you can use ptr_map but not the indexing operator. When inserting, use insert, when looking up a key, use find.

Upvotes: 2

ltjax
ltjax

Reputation: 15997

Your base class does not need to be realisable. See the example here: http://www.boost.org/doc/libs/1_49_0/libs/ptr_container/doc/tutorial.html#associative-containers The animal there does have abstract functions! In fact, this is one of the primary uses for ptr containers. Your error is probably caused elsewhere and you need to post more code.

Also:

This seems very messy though, and I'd rather store all the objects in a single polymorphic map that owns and memory-manages the contained objects.

I don't think it is. It can actually be beneficial to never "lose" the type of the actual object, in case you need that later. For example, if you wanted to do something for all Triangles, but not all other shapes, this will come in handy.

The disadvantage is of course that you need to keep all the maps in sync, but this is very easy to solve: Stick those 4 maps (and preferably no other data members!) into the private section of a class and implement 3 overloads for the different types and always insert them into the strongly typed "owning" map and into the map with polymorphic pointers. I will be very easy to keep them in sync behind a small class interface. Just don't try to do it as an unstructured part of something bigger, or the code will start to look messy.

Upvotes: 1

Related Questions