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(void)
{
container c;
for (int i = 0; i < 100; ++i)
c.push_back(new Derived);
} // leaks here!
int main(void)
{
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 *p)
{
delete p;
}
void foo(void)
{
container c;
for (int 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(void)
{
foo();
}
This is unruly, though. Also, 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.
Better would be if the pointers deleted themselves. The standard library provides std::auto_ptr
, but this won't do, due to copy issues. You'd need an improved smart pointer, like boost::shared_ptr
:
#include <boost/shared_ptr.hpp>
#include <vector>
struct Base
{
virtual ~Base() {}
};
struct Derived : Base
{
};
// hold smart pointers
typedef boost::shared_ptr<Base> BasePtr;
typedef std::vector<BasePtr> container;
void foo(void)
{
container c;
for (int i = 0; i < 100; ++i)
c.push_back(BasePtr(new Derived));
// free memory
// done automatically
}
int main(void)
{
foo();
}
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>
#include <vector>
struct Base
{
virtual ~Base() {}
};
struct Derived : Base
{
};
// hold pointers, specially
typedef boost::ptr_vector<Base*> container;
void foo(void)
{
container c;
for (int i = 0; i < 100; ++i)
c.push_back(new Derived);
// free memory
// done automatically
}
int main(void)
{
foo();
}
On this basic abstract, ptr_vector
and std::vector<boost::shared_ptr>
look quite similar; indeed, they will behave the same.
Because performance "matters" in a game, a ptr_container
might be slightly better. A shared_ptr
carries a bit of extra memory with it. The reason I put matters into quotes is because you probably won't notice the different between the two. You can't optimize something until you know it's a problem, so I'm actually making an assumption that a ptr_vector
would be better: I don't know for sure, since we haven't tried it and profiled it.
Personally, though, I would go with a vector of smart pointers. The reason is that once you start using the vector, you could do things such as store the pointer, but remove it from the vector. It's also more RAII- and abstraction-like: The vector holds objects, unknowing what they are or how they work, and the pointers clean up after themselves.
With a ptr_vector
, the act of removing a pointer from the vector means what it was pointing to is also deleted, so you can't do something like this safely:
Enemy *e = c.back();
c.pop_back(); // the enemy pointed to by e is deleted, e is now invalid
However, you can do this with a shared_ptr
:
typedef boost::shared_ptr<Enemy> EnemyPtr;
EnemyPtr e = c.back();
c.pop_back(); // the enemy is still valid because e holds a reference to it
Because it is reference counted. When you remove it from the vector, the reference count goes down by 1, but will still be at least 1, but you are holding a reference. Once that smart pointer goes out, it will be deleted.