例如,我有像这样的代码:
vector<Enemy*> Enemies;
我将从Enemy类派生出一个类,然后动态分配派生类的内存,就像这样:
enemies.push_back(new Monster());
我需要注意哪些事项以避免内存泄漏和其他问题?
vector<Enemy*> Enemies;
我将从Enemy类派生出一个类,然后动态分配派生类的内存,就像这样:
enemies.push_back(new Monster());
我需要注意哪些事项以避免内存泄漏和其他问题?
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标准中由于疏忽被遗漏了,但是你可以自己创建一个。如果你能使用make_unique
,不建议直接创建unique_ptr
,可以按照以下方式进行:
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_ptr
,当最后一个 shared_ptr
停止存在(计数变为零)时,它释放指针。复制只是增加引用计数(移动则以更低的成本转移所有权)。您可以使用 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();
}
虽然在C++03中,boost::ptr_vector<T>
有明显的用途,但我无法确定它现在是否还具有相关性,因为我们可以使用std::vector<std::unique_ptr<T>>
,可能几乎没有可比较的开销,但这种说法应该经过测试。
无论如何,在你的代码中永远不要明确释放资源。封装起来以确保自动处理资源管理。你的代码中不应该有任何拥有原始指针。
作为游戏中的默认设置,我可能会选择std::vector<std::shared_ptr<T>>
。我们预计会进行共享,直到分析表明需要改变时,它足够快速、安全且易于使用。
shared_ptr
在CPU/GPU密集型3D应用程序中胜过使用特殊目的分配器的自定义智能指针。在您进行测量之前,您永远不会知道... - sbistd::unique_ptr
而不是std::auto_ptr
或boost::shared_ptr
。 - beldazvector<T*>
的问题在于,每当向量意外地超出范围(例如抛出异常时),向量会为您清理,但这只会释放用于保存指针的内存,而不是您为指针引用的内容分配的内存。因此,GMan的delete_pointed_to
函数的价值有限,因为它仅在没有错误发生时起作用。vector< std::tr1::shared_ptr<Enemy> > Enemies;
(如果您的标准库没有TR1,请使用boost :: shared_ptr
。)
除了非常罕见的角落情况(循环引用)外,这只是消除了对象生命周期的麻烦。
编辑:请注意,GMan在他详细的答案中也提到了这一点。
delete_pointer_to
的可能性,而不详细说明它,因为它太低效了。我觉得有必要把现成的解决方案放入一个简短、简单的“这样做”的答案中。 (Boost的指针容器是一个不错的选择,虽然我确实给了它们一个赞。)如果你感到被误读了,我很抱歉。 - sbi:)
- sbi除了使用智能指针之外,还有其他方法可以解决这些内存泄漏问题。一种方法是编写自己略微修改过的std::unique()版本,称为“your_company::unique()”。基本技巧是,不是复制元素,而是交换两个元素。并且您必须确保不是比较两个指针,而是调用BinaryPredicate跟随两个指针到对象本身,并比较这两个“Monster”派生对象的内容。
1)@SEE_ALSO: http://www.cplusplus.com/reference/algorithm/unique/
2)@SEE_ALSO: 什么是消除重复项和对向量进行排序的最有效方法?
第二个链接写得非常好,适用于std::vector,但存在内存泄漏、重复释放(有时会导致段错误)的问题。
3)@SEE_ALSO: valgrind(1)。这个LINUX上的“内存泄漏”工具非常神奇!我强烈建议使用它!
我希望在未来的文章中发布一个漂亮的版本 "my_company::unique()"。目前,它还不完美,因为我想让具有BinaryPredicate的3个参数版本无缝地适用于函数指针或FUNCTOR,并且我在处理两者时遇到了一些问题。如果我无法解决这些问题,我将发布我所拥有的,并让社区尝试改进我已经完成的部分。
boost::smart_ptr
。 - beldaz