How to Avoid Memory Leaks When Using a Vector of Pointers to Dynamically Allocated Objects in C++

How to avoid memory leaks when using a vector of pointers to dynamically allocated objects in C++?

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.

vector of pointer to object - how to avoid memory leak?

Yes, you have to do that to avoid memory leak. The better ways to do that are to make a vector of shared pointers (boost, C++TR1, C++0x, )

 std::vector<std::tr1::shared_ptr<A> > l;

or vector of unique pointers (C++0x) if the objects are not actually shared between this container and something else

 std::vector<std::unique_ptr<A>> l;

or use boost pointer containers

  boost::ptr_vector<A> l;

PS: Don't forget A's virtual destructor, as per @Neil Butterworth!

Can I encounter memory leaks when using vectors of classes? (C++)

No, you won't encounter directly a memory leaks related to the vector this way. Indeed, objects is a variable with automatic storage duration. What this means is that the variable you created will live in the scope you created it. If you've created it in a function, and if/for/while/etc scope or even a raw block scope, it will be cleaned up at the end of this same scope, without the need for any actions from your part.

Then, nothing is preventing your class itself from leaking, e.g. if you have ownership of some memory and don't release it as an instance of your class go away.

Vector of pointers to Objects - Memory leaks

delete Users;   // Users is an array from the heap

Well then that's wrong. Should be

delete [] Users;

Anything you new with [] gets a delete with [].

On a side note; do you really need a vector of pointers here? It's pretty darn rare to actually need that (though you see it a lot) and it completely negates the container's ability to manage memory for you, requiring you to loop through and deallocate every element.

vectors use dynamic memory behind the scenes for each element. You could us a vector of smart pointers or even a vector<vector<T>> (though, if performance is of the utmost concern, a jagged array may be a better choice. Don't assume that though).

clearing a vector of pointers

Yes, the code has a memory leak unless you delete the pointers. If the foo class owns the pointers, it is its responsibility to delete them. You should do this before clearing the vector, otherwise you lose the handle to the memory you need to de-allocate.

   for (auto p : v)
{
delete p;
}
v.clear();

You could avoid the memory management issue altogether by using a std::vector of a suitable smart pointer.

Pushing non dynamically allocated objects to a vector of pointers

"Traversing" pointers that are not pointing at objects is undefined behavior in C++.

The pointer doesn't have to point at a dynamically allocated object, but that is typical. The pointer could point at an automatic storage object. For example:

Node bob;
Node* root = &bob;

however, in every case, you are responsible for managing lifetime of these objects. Automatic storage objects survive until the end of the current scope, temporary objects until the end of the current full expression, and dynamically allocated objects until they are deleted.

Smart pointers can help a bit with lifetime management issues, but they only help. They do not solve it, and you still have to be aware of lifetime.

Many post-C++ languages have automatic garbage collection that makes lifetime less of the programmers problem. You do not have this help in C++.

What's the most efficient way to free the memory of pointers in a std::vector?

I'm pretty sure you don't need those dynamic allocations. You can delegate to std::vector the allocation and deallocation of the cell elements.

Try creating a minefield class that is a "matrix view" of a linear data allocated with a std::vector.

#include <vector>
#include <iostream>

struct cell
{
std::size_t i, j;
bool mine;
};

class minefield
{
public:
minefield(int rows, int columns): cells(rows*columns), rowSize(columns)
{
std::cout << "just allocated std::vector\n";
}
virtual ~minefield()
{
std::cout << "about to free std::vector\n";
}
bool stepOn(std::size_t i, std::size_t j)
{
return cells.at(i*rowSize + j).mine;
}
void set(cell c)
{
cells[c.i*rowSize + c.j] = c;
}
private:
std::vector<cell> cells;
int rowSize;
};


void funct() {
minefield mf(5, 5);
mf.set(cell{0, 0, false});
mf.set(cell{0, 1, true});

if (not mf.stepOn(0, 0))
{
std::cout << "still alive :)\n";
}
if (mf.stepOn(0, 1))
{
std::cout << "dead XP\n";
}
}
int main()
{
funct();
std::cout << "bye!!\n";
}

This should print:

just allocated std::vector

still alive :)

dead XP

about to free std::vector

bye!!

What do I need to do before deleting elements in a vector of pointers to dynamically allocated objects?

  1. Yes
  2. Vectors are implemented using template memory allocators that take care of the memory management for you, so they are somewhat special. But as a general rule of thumb, you don't have to call delete on variables that aren't declared with the new keyword because of the difference between stack and heap allocation. If stuff is allocated on the heap, it must be deleted (freed) to prevent memory leaks.
  3. No. You explicitly have to call delete myVec[index] as you iterate over all elements.

Ex:

for(int i = 0; i < myVec.size(); ++i)
delete myVec[i];

With that said, if you're planning on storing pointers in a vector, I strongly suggest using boost::ptr_vector which automatically takes care of the deletion.



Related Topics



Leave a reply



Submit