views:

1126

answers:

7

If I have the following code

Foo *f = new Foo();
vector<Foo*> vect;
vect.push_back(f);
// do stuff
vect.erase(f);

Did I create a memory leak ? I guess so, but the word erase gives the feeling that it is deleting it.

Writing this, I am wondering if it is not a mistake to put a pointer in a STL vector. What do you think ?

+7  A: 

Yes, you created a memory leak by that. std::vector and other containers will just remove the pointer, they won't free the memory the pointer points to.

It's not unusual to put a pointer into a standard library container. The problem, however, is that you have to keep track of deleting it when removing it from the container. A better, yet simple, way to do the above, is to use boost::shared_ptr:

{ 
    boost::shared_ptr<foo> f(new foo);

    std::vector< boost::shared_ptr<foo> > v;
    v.push_back(f);
    v.erase(v.begin());
} /* if the last copy of foo goes out of scope, the memory is automatically freed */

The next C++ standard (called C++1x and C++0x commonly) will include std::shared_ptr. There, you will also be able to use std::unique_ptr<T> which is faster, as it doesn't allow copying. Using std::unique_ptr with containers in c++0x is similar to the ptr_container library in boost.

Johannes Schaub - litb
Be very careful to use boost::shared_ptr, as this answer instructs, instead of auto_ptr. STL containers cannot be used at all with auto_ptr (the reason is a bit long to explain in this comment).
Gorpik
Ok, I take note of this. Thanks !
Barth
+1  A: 

It is definitely not a mistake to point a pointer into a standard container (it's a mistake to make a container of auto_ptr's however). Yes, you do need to explicitly delete to free the memory pointed to by the individual elements, or you can use one of the boost smart pointers.

David Nehme
+4  A: 

Another option is to use the Boost Pointer Containers. They are designed to do exactly what you want.

KeithB
+2  A: 

Alternatively there is the boost::ptr_vector container.

It knows it is holding pointers that it owns and thus auto deletes them.

As a nice side affect, when accessing elements it returns a reference to the object not the pointer to make the code look nice.

Foo *f = new Foo();
boost::ptr_vector<Foo>  vect;
vect.push_back(f);
// do stuff
vect.erase(f);
Martin York
Great reference, thanks for that. It just so happens that I spend the last few days refactoring containers-of-ptrs data structures, boost::indirect_iterator has been helpful but these ptr_xxx containers would be even better I think... Maybe I'll need to refactor again ;)
Roel
+1  A: 

vector deletes the data it contains. Since your vector contains pointers, it only deletes the pointers, not the data they may or may not point to.

It's a pretty general rule in C++ that memory is released where it was allocated. The vector did not allocate whatever your pointers point to, so it must not release it.

You probably shouldn't store pointers in your vector in the first place. In many cases, you would be better off with something like this:

vector<Foo> vect;
vect.push_back(Foo());
// do stuff
vect.erase(f);

Of course this assumes that Foo is copyable, and that its copy constructor is not too expensive, but it avoids memory leaks, and you don't have to remember to delete the Foo object. Another approach would be to use smart pointers (such as Boost's shared_ptr), but you may not need pointer semantics at all, in which case the simple solution is the best one.

jalf
+1  A: 

STL containers will not free your memory. The best advice is using smart pointers, knowing that std::auto_ptr will not fit inside containers. I would recommend boost::shared_ptr, or if your compiler vendor has support for TR1 extensions (many do) you can use std::tr1::shared_ptr.

Also note that the vector will not even free the internal memory reserved for the pointer. std::vectors never downsize not even with a call to clear(). If you need to downsize a vector you will have to resort to creating another vector and swapping contents.

David Rodríguez - dribeas
+1  A: 

To clarify why the pointer is not deleted, consider

std::vector<char const*> strings;
strings.push_back("hello");
strings.push_back("world");

std::vector<int*> arrays;
strings.push_back(new int[10]);
strings.push_back(new int[20]);

std::vector<unsigned char*> raw;
strings.push_back(malloc(1000));
strings.push_back(malloc(2000));
MSalters