std::vector 会像往常一样为您管理内存,但该内存将是指针,而不是对象。
这意味着一旦你的向量超出范围,你的类就会丢失在内存中。例如:
#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();
}
您需要做的是确保在向量超出范围之前删除所有对象:
#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();
}
但这很难维护,因为我们必须记住执行一些操作。更重要的是,如果在元素分配和释放循环之间发生异常,释放循环将永远不会运行,并且无论如何您都会遇到内存泄漏!这称为异常安全,这是需要自动完成释放的关键原因。
如果指针自己删除会更好。这些被称为智能指针,标准库提供std::unique_ptr和std::shared_ptr。
std::unique_ptr 表示指向某个资源的唯一(非共享、单一所有者)指针。这应该是您的默认智能指针,并且可以完全替代任何原始指针的使用。
auto myresource = /*std::*/make_unique<derived>(); // won't leak, frees itself
std::make_unique 因疏忽而在 C++11 标准中缺失,但您可以自己制作一个。要直接创建unique_ptr(如果可以,不推荐超过make_unique),请执行以下操作:
std::unique_ptr<derived> myresource(new derived());
唯一指针只有移动语义;它们不能被复制:
auto x = myresource; // error, cannot copy
auto y = std::move(myresource); // okay, now myresource is empty
这就是我们需要在容器中使用它的全部内容:
#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 具有引用计数复制语义;它允许多个所有者共享对象。它跟踪一个对象存在多少shared_ptrs,当最后一个不存在时(该计数变为零),它释放指针。复制只是增加了引用计数(并以更低、几乎免费的成本转移所有权)。您可以使用std::make_shared 来创建它们(或直接如上所示,但因为shared_ptr 必须在内部进行分配,所以使用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();
}
请记住,您通常希望使用std::unique_ptr 作为默认值,因为它更轻量级。此外,std::shared_ptr 可以由 std::unique_ptr 构建(但反之不行),因此可以从小处着手。
或者,您可以使用创建的容器来存储指向对象的指针,例如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();
}
虽然 boost::ptr_vector<T> 在 C++03 中有明显的用途,但我现在无法谈论相关性,因为我们可以使用 std::vector<std::unique_ptr<T>> 可能几乎没有可比开销,但应该测试这种说法。
无论如何,永远不要在你的代码中明确地释放东西。总结一下以确保自动处理资源管理。您的代码中不应包含原始的拥有指针。
作为游戏的默认设置,我可能会选择std::vector<std::shared_ptr<T>>。无论如何,我们希望共享,它足够快,直到配置文件另有说明,它是安全的,并且易于使用。