77

I'm using a vector of pointers to objects. These objects are derived from a base class, and are being dynamically allocated and stored.

For example, I have something like:

vector<Enemy*> Enemies;

and I'll be deriving from the Enemy class and then dynamically allocating memory for the derived class, like this:

enemies.push_back(new Monster());

What are things I need to be aware of to avoid memory leaks and other problems?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
akif
  • 12,034
  • 24
  • 73
  • 85
  • Maybe a native English speaker can decipher what you want to say, but I'm lost. First, you're talking about memory leaks -> language/platform dependent; I expect you mean C++. Avoiding memory leaks has been discussed extensively already (http://stackoverflow.com/search?q=c%2B%2B+raii). You need a virtual destructor for deleting from a base type to be working correctly. – gimpf Sep 01 '09 at 08:04
  • 1
    What do you mean by "vectors to pointers"? Do you mean "vectors **of** pointers"? – Tamás Szelei Sep 01 '09 at 08:04
  • yes, I'm using C++. Yes, I do mean vectors of pointers. Sorry for my bad English – akif Sep 01 '09 at 08:11
  • 17
    I took a shot at rewording it all, please edit or comment if I've removed any information, or if it's not clear. – GManNickG Sep 01 '09 at 08:35
  • Only that you need to delete each element of the vector of pointers to new classes defined within the vector. The vector container itself will be deallocated automatically when it goes out of scope. Note if your inheritance hierarchy is virtual, then you need to explicitly define your destructors, as that may also cause memory leaks. – Owl Jun 28 '16 at 15:56

4 Answers4

164

std::vector will manage the memory for you, like always, but this memory will be of pointers, not objects.

What this means is that your classes will be lost in memory once your vector goes out of scope. For example:

#include <vector>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

typedef std::vector<base*> container;

void foo()
{
    container c;

    for (unsigned i = 0; i < 100; ++i)
        c.push_back(new derived());

} // leaks here! frees the pointers, doesn't delete them (nor should it)

int main()
{
    foo();
}

What you'd need to do is make sure you delete all the objects before the vector goes out of scope:

#include <algorithm>
#include <vector>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

typedef std::vector<base*> container;

template <typename T>
void delete_pointed_to(T* const ptr)
{
    delete ptr;
}

void foo()
{
    container c;

    for (unsigned i = 0; i < 100; ++i)
        c.push_back(new derived());

    // free memory
    std::for_each(c.begin(), c.end(), delete_pointed_to<base>);
}

int main()
{
    foo();
}

This is difficult to maintain, though, because we have to remember to perform some action. More importantly, if an exception were to occur in-between the allocation of elements and the deallocation loop, the deallocation loop would never run and you're stuck with the memory leak anyway! This is called exception safety and it's a critical reason why deallocation needs to be done automatically.

Better would be if the pointers deleted themselves. Theses are called smart pointers, and the standard library provides std::unique_ptr and std::shared_ptr.

std::unique_ptr represents a unique (unshared, single-owner) pointer to some resource. This should be your default smart pointer, and overall complete replacement of any raw pointer use.

auto myresource = /*std::*/make_unique<derived>(); // won't leak, frees itself

std::make_unique is missing from the C++11 standard by oversight, but you can make one yourself. To directly create a unique_ptr (not recommended over make_unique if you can), do this:

std::unique_ptr<derived> myresource(new derived());

Unique pointers have move semantics only; they cannot be copied:

auto x = myresource; // error, cannot copy
auto y = std::move(myresource); // okay, now myresource is empty

And this is all we need to use it in a container:

#include <memory>
#include <vector>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

typedef std::vector<std::unique_ptr<base>> container;

void foo()
{
    container c;

    for (unsigned i = 0; i < 100; ++i)
        c.push_back(make_unique<derived>());

} // all automatically freed here

int main()
{
    foo();
}

shared_ptr has reference-counting copy semantics; it allows multiple owners sharing the object. It tracks how many shared_ptrs exist for an object, and when the last one ceases to exist (that count goes to zero), it frees the pointer. Copying simply increases the reference count (and moving transfers ownership at a lower, almost free cost). You make them with std::make_shared (or directly as shown above, but because shared_ptr has to internally make allocations, it's generally more efficient and technically more exception-safe to use make_shared).

#include <memory>
#include <vector>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

typedef std::vector<std::shared_ptr<base>> container;

void foo()
{
    container c;

    for (unsigned i = 0; i < 100; ++i)
        c.push_back(std::make_shared<derived>());

} // all automatically freed here

int main()
{
    foo();
}

Remember, you generally want to use std::unique_ptr as a default because it's more lightweight. Additionally, std::shared_ptr can be constructed out of a std::unique_ptr (but not vice versa), so it's okay to start small.

Alternatively, you could use a container created to store pointers to objects, such as a boost::ptr_container:

#include <boost/ptr_container/ptr_vector.hpp>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

// hold pointers, specially
typedef boost::ptr_vector<base> container;

void foo()
{
    container c;

    for (int i = 0; i < 100; ++i)
        c.push_back(new Derived());

} // all automatically freed here

int main()
{
    foo();
}

While boost::ptr_vector<T> had obvious use in C++03, I can't speak of the relevance now because we can use std::vector<std::unique_ptr<T>> with probably little to no comparable overhead, but this claim should be tested.

Regardless, never explicitly free things in your code. Wrap things up to make sure resource management is dealt with automatically. You should have no raw owning pointers in your code.

As a default in a game, I would probably go with std::vector<std::shared_ptr<T>>. We expect sharing anyway, it's fast enough until profiling says otherwise, it's safe, and it's easy to use.

knedlsepp
  • 6,065
  • 3
  • 20
  • 41
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • 2
    If he's actually writing gamecode (as the example kind of alludes to) then a ref counted pointer (or however boost implemented the shared pointer) is likely overly expensive.. a constant memory footprint (especially for AI objects) is a loftier design goal than removing a for loop to deallocate. – Dan O Sep 01 '09 at 09:03
  • Which one should I choose b/w Pointer Contains and Shared Pointers and why? – akif Sep 01 '09 at 09:28
  • 5
    @Dan: Some way or another you will have to do the cleanup and if that's too slow, the question isn't which way to do it, but how to avoid having to do it in the first place. If you can't get around it, use the cleanest way first, then measure, and only try to improve afterwards. Boost means several thousand pairs of keen eyes improving the code. Hard to beat that: I have seen boost's `shared_ptr` outperforming a custom smart pointer using a special-purpose allocator in CPU/GPU-intensive 3D applications. Until you measure, you never know... – sbi Sep 01 '09 at 09:44
  • Updated my answer. Luckily our 'answers' matched this time, sbi. :P (Profile!) – GManNickG Sep 01 '09 at 09:47
  • GMan, I wasn't aware that our answers matching is something to note. `:^>` I usually find myself agreeing with you and you're among those who's answers I value (and upvote regularly). Have we disagreed a lot recently? – sbi Sep 01 '09 at 10:27
  • Ha, no I was referring to my comments on your answer below on my unsureness to edit in your information. My comment isn't an issue if your information is my answers. Lame connection. :P Anyway, thank you for the compliment. – GManNickG Sep 01 '09 at 10:33
  • @GMan: Now you have me totally confused. `:o>` I guess I'll just stick to "isn't an issue" then, OK? – sbi Sep 01 '09 at 12:18
  • Thanks sir, really helpful and detailed answer – akif Sep 01 '09 at 12:24
  • 1
    @sbi I'm not advocating a different shared_ptr, I'm advocating a different approach to memory management. Shared pointers are very likely inappropriate in the game code case. In fact, they're totally inappropriate for the example the original poster submitted. Most of my argument is summarized here: http://www.bureau14.fr/blogea/2009/08/smart-pointers-are-overused/ – Dan O Sep 02 '09 at 01:38
  • @Dan: The article you linked to had (besides sound advice to not to mindlessly copy around smart pointers) a nice example of a dynamically allocated object that lives for the applications lifetime. I suppose that's quite different from the most likely constantly changing number of enemies in a computer game. These things will most likely _have_ to be allocated dynamically and a way has to be found to manage their lifetime. Smart pointers are (IMO) the default off-the-shelf solution to this and I wouldn't use anything else unless profiling forces me to. – sbi Sep 02 '09 at 09:17
  • This answer is referenced by others, but it's a bit dated now. It might be worth revising to mention `std::unique_ptr` instead of `std::auto_ptr` or `boost::shared_ptr`. – beldaz Feb 14 '13 at 01:48
  • i'm getting a segfault when deleting an element of the vector, although I allocate it with new. (I can't use smart pointers) – somerandomdev49 Apr 04 '20 at 13:11
  • @CoolMikeWhoDoesSomething: I'd recommend asking a new question for your issue. Note that if you don't have any smart pointers readily available, it is _highly_ worth the time to write your own unique_ptr and go from there. – GManNickG Apr 04 '20 at 19:51
  • @GManNickG I don't want to make a question, because my code is too complicated to put in a stackoverflow question and I don't think that the problem will continue to exist if I make the code simple. Although thanks for your advice, because my own smart pointers will be very nice for the project I'm working on :) – somerandomdev49 Apr 07 '20 at 13:34
10

The trouble with using vector<T*> is that, whenever the vector goes out of scope unexpectedly (like when an exception is thrown), the vector cleans up after yourself, but this will only free the memory it manages for holding the pointer, not the memory you allocated for what the pointers are referring to. So GMan's delete_pointed_to function is of limited value, as it only works when nothing goes wrong.

What you need to do is to use a smart pointer:

vector< std::tr1::shared_ptr<Enemy> > Enemies;

(If your std lib comes without TR1, use boost::shared_ptr instead.) Except for very rare corner cases (circular references) this simply removes the trouble of object lifetime.

Edit: Note that GMan, in his detailed answer, mentions this, too.

Community
  • 1
  • 1
sbi
  • 219,715
  • 46
  • 258
  • 445
  • 1
    @GMan: I completely read your answer and saw this. I would have only mentioned the `delete_pointer_to` possibility without elaborating on it, since it's so much inferior. I felt the need to put the off-the-shelf solution into a short, simple "do-it-this-way" answer. (Boost's pointer containers are a nice alternative, though, and I did give an upvote for mentioning them.) I'm sorry if you felt misread. – sbi Sep 01 '09 at 09:14
  • 2
    I think your point is very good, actually. Should I edit it in? I'm always unsure at this point. If I edit my answer so it's more complete, I feel like I'm "stealing" rep from other people. – GManNickG Sep 01 '09 at 09:26
  • 4
    @GMan: Go ahead and improve the answer that's on top of the stack. Your answer is good and detailed and definitley deserves to be there. To hell with the rep, if there's one less programmer out there doing this kind of things, that will help us all a lot more than any rep points. `:)` – sbi Sep 01 '09 at 09:37
  • and perhaps will help others in future, thus saving others time :) – akif Sep 01 '09 at 09:39
  • 2
    My word! Friendly and cooperative discourse, let alone *agreement* in an online discussion? Totally unheard of! Nice work :) – e.James Nov 19 '09 at 06:08
9

I am assuming following:

  1. You are having a vector like vector< base* >
  2. You are pushing the pointers to this vector after allocating the objects on heap
  3. You want to do a push_back of derived* pointer into this vector.

Following things come to my mind:

  1. Vector will not release the memory of the object pointed to by the pointer. You have to delete it itself.
  2. Nothing specific to vector, but the base class destructor should be virtual.
  3. vector< base* > and vector< derived* > are two totally different types.
Naveen
  • 74,600
  • 47
  • 176
  • 233
  • Your assumptions are absolutely correct. Sorry, I wasn't able to explain properly. Is there anything else? – akif Sep 01 '09 at 08:14
  • 1
    If possible avoid raw pointers, and use the methods described in GMan's answer. – Naveen Sep 01 '09 at 08:34
-1

One thing to be very careful is IF there are two Monster() DERIVED objects whose contents are identical in value. Suppose that you wanted to remove the DUPLICATE Monster objects from your vector (BASE class pointers to DERIVED Monster objects). If you used the standard idiom for removing duplicates (sort, unique, erase: see LINK #2], you will run into memory leak issues, and/or duplicate delete problems, possibly leading to SEGMENTATION VOIOLATIONS (I have personally seen these problems on LINUX machine).

THe problem with the std::unique() is that the duplicates in the [duplicatePosition,end) range [inclusive, exclusive) at the end of the vector are undefined as ?. What can happen is that those undefined ((?) items might be extra duplicate or a missing duplicate.

The problem is that std::unique() isn't geared to handle a vector of pointers properly. The reason is that std::unique copies uniques from the end of the vector "down" toward the beginning of the vector. For a vector of plain objects this invokes the COPY CTOR, and if the COPY CTOR is written properly, there is no problem of memory leaks. But when its a vector of pointers, there is no COPY CTOR other than "bitwise copy", and so the pointer itself is simply copied.

THere are ways to solve these memory leak other than using a smart pointer. One way to write your own slightly modified version of std::unique() as "your_company::unique()". The basic trick is that instead of copying an element, you would swap two elements. And you would have to be sure that the instead of comparing two pointers, you call BinaryPredicate that follows the two pointers to the object themselves, and compare the contents of those two "Monster" derived objects.

1) @SEE_ALSO: http://www.cplusplus.com/reference/algorithm/unique/

2) @SEE_ALSO: What's the most efficient way to erase duplicates and sort a vector?

2nd link is excellently written, and will work for a std::vector but has memory leaks, duplicate frees (sometimes resulting in SEGMENTATION violations) for a std::vector

3) @SEE_ALSO: valgrind(1). THis "memory leak" tool on LINUX is amazing in what it can find! I HIGHLY recommend using it!

I hope to post a nice version of "my_company::unique()" in a future post. Right now, its not perfect, because I want the 3-arg version having BinaryPredicate to work seamlessly for either a function pointer or a FUNCTOR, and I'm having some problems handling both properly. IF I cannot solve those problems, I'll post what I have, and let the community have a go at improving on what I have done so far.

Community
  • 1
  • 1
  • This doesn't seem to answer the question at all. If all you are concerned about is the possibility of multiple pointers to the same object you should just use a reference-counted smart pointer, such as the `boost::smart_ptr`. – beldaz Feb 14 '13 at 01:53