views:

308

answers:

6

I have a map that relates integers to vectors (of objects). These vectors represent a set of tasks to perform. In order to reduce the amount of copying going on while using this map and vector I've set them up to make use of pointers.

std::map<int, std::vector<MyObject *> *> myMap;

During initialization of the class that holds myMap, I populate myMap by creating a new vector filled with new MyObjects.

What I'm concerned with, however, is memory management. Now I have these various objects sitting out on the heap somewhere and I am responsible for cleaning them up when I'm done with them. I also know that I will NEVER be done with them until the program is finished. But what about in 10 weeks when someone decides that a clever way to modify this app involves removing items from the map/vectors. This would cause a memory leak.

My question is how can I handle the proper deallocation of these objects so that even if they get removed via an STL function that the objects get successfully deallocated?

Your help is much appreciated, let me know if I've missed anything critical! Thanks!

+6  A: 

Use a smart pointer boost:shared_ptr rather than raw pointers, that way when the object is destroyed it will clear up the heap allocated memory as well.

boost::shared_ptr http://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/shared_ptr.htm

Also is there really a reason to have pointers to the vectors? They take up almost no space, and objects within an std::map are not moved anyway (unlike the objects in a vector which are moved/copied each time the vector reallocates, eg to get more space).

EDIT: Also shared_ptr is a component of tr1, and I'm pretty sure is in the next standard, so your compiler may already have it. There are also lots of other smart pointers around that are STL safe to give you an idea of how to write your own, a quick search on Google should find them.

EDIT2: Just checked and the Visual Studio 2008 implementation of TR1 includes the shared_ptr which is included in the Visual C++ 2008 Feature Pack. I expect many other vendors have implementations available for at least parts of TR1, so if your not using VS search your vendors site for TR1 support.

Fire Lancer
I was hoping for a solution that avoided the use of new libraries if possible. Any new libraries have to be vetted unfortunately. I'll look into this though as a backup!
Brian
Well boost is a great thing to get on your project, there are load of useful things in it. However a smart pointer is something you could write by your self. I'll update my post with a quick one that may be suitable for your needs.
Fire Lancer
Thanks for the tip on the maps, if there's no finagling of the data inside the map I might be able to get away from pointing at the vector which makes this that much easier to deal with.
Brian
Also note that shared_ptr is only a "new library" if it's 2003. It's about the least controversial thing in TR1, which itself is as close as possible to the C++ standard without touching. Permitting arbitrary Boost libraries into your project is probably not a good idea (not least because not all Boost libraries have stable interfaces yet). shared_ptr is a fairly safe bet, though, and well worth vetting since it's designed for exactly this purpose. Allowing it doesn't mean you have to take all of Boost.
Steve Jessop
@onebyone I think he meant "new" as in "additional"
Draemon
Sorry, yes, I was being a bit over-dramatic. I just mean that shared_ptr should not be considered a radical addition to C++, nor is it a leap of faith into third-party library dependency.
Steve Jessop
I'll take a look at some of the google searches and figure out which compiler we're using (Not Visual Studio as we're on a Solaris/Embedded environment), and I also imagine it's quite old, the code base is also very old, so we probably don't have it. I'll check around with some other devs too to see if we've already included/have vetted this library.
Brian
To quote C++ Coding Standards (http://www.amazon.com/Coding-Standards-Guidelines-Practices-Depth/dp/0321113586): If you don't use Boost, use Boost.SharedPtr :)
+1  A: 

Using a shared pointer (as suggested by others) is the best solution.

If you really know you will never be done with them, then they don't technically need deallocating. If this really is desired behaviour, just document it so that someone doesn't come along in 10 weeks and mistake this for a genuine leak.

Draemon
Well, in the spirit of designing for the future, I'd like to make sure memory management is as tight as it can be. I can't foresee what will be done with this code in the future as it is an incredibly complex system.
Brian
I wouldn't do that... Chances are your documentation will get out of sync with what's really going on in the code and then you'll have docs that say don't clean up and code that does clean up... IMHO it would be better to fix the code, using a shared pointer as suggested by Fire Lancer. The fix is in one place, it can't get out of sync with what the code is doing (as it IS the code) and it documents the ownership semantics of the collection quite clearly.
Len Holgate
It was just because you stressed that the object would always be in use. If you keep the comment next to the allocation it reduces the chance of it getting out of sync. However, using a shared pointer is certainly the better solution.
Draemon
+4  A: 

I agree the use of smart pointers is a good way to go, but there are at least two alternatives:

a) Copying may not be as expensive as you think it is. Try implementing a map of values

std::map<int, std::vector<MyObject>> myMap;

b) Replace the vector with a class of your own that wraps the vector. In that classes destructor, handle the deallocation. You could also provide methods for adding and removing MyObjects.

anon
I'll try and do some benchmarking on both the vector with values and one with pointers.
Brian
Note that copying will inevitably be more expensive - the question is, is it too expensive _in the context of your application_? A simple independant benchmark probably won't help much here.
anon
I also seem to remember some other implications of filling a vector with objects versus pointers. Some kind of trouble I had with a previous app.
Brian
There shouldn't be any problems as far as the language is concerned - I use values in preference to pointers all the time.
anon
A: 

If the ownership of each pointer is not shared among different entries in the vectors/maps and so you only mean reducing the copying done at insertion time, then you should also consider boost's Pointer Container library.

TheUndeadFish
+1  A: 

Thank you all for the good answers. I think currently I'm leaning towards a vector of values solution currently. The main reason is that std::auto_ptr doesn't work with collections due to the fact that it is uncopyable. This would be the only implementation of a smart pointer that I would be able to use without going through a burdensome vetting process or rolling my own.

The good news is your responses led me down a very good road. I learned about RAII, dangers about exception handling and how to minimize them, and put enough vigilance into my design that I can be satisfied with its "Correctness".

Attached are some links that I found helpful along the way. I hope that anyone coming to a similar problem will find these links helpful.

RAII Resource
Smart Pointers in C++
Boost Smart Pointers
More background/implementation details about Smart pointers

Brian
A: 

To elaborate on minimizing copying when using map<,vector<Object>>:

Closely look at the interfaces of map and vector. They mostly return references to the contained items, and if you preserve the reference in passing these things around, no copying will occur.

Bad example:

std::vector<MyObject> find_objects( const std::map<int,std::vector<MyObject>> & map, int i ) {
    const std::map<int,std::vector<MyObject>>::const_iterator it = map.find( i );
    if ( it != map.end() )
        return it->second;
    else
        return std::vector<MyObject>();
}
// ...
const std::vector<MyObject> objects = find_objects(/*...*/);

Better:

const std::vector<MyObject> & find_objects( const std::map<int,std::vector<MyObject>> & map, int i ) {
    const std::map<int,std::vector<MyObject>>::const_iterator it = map.find( i );
    if ( it != map.end() )
        return it->second;
    static const std::vector<MyObject> none();
    return none;
}
// ...
const std::vector<MyObject> & objects = find_objects(/*...*/);

-> no copying