C++不像java和c#那样有垃圾回收机制,所以内存泄露是个比较头疼的问题(谁会忘记写delete?谁都会!),有几种缓解之道:
1)智能指针(推荐boost::shared_ptr)。
2)真正的垃圾回收,参考http://www.hpl.hp.com/personal/Hans_Boehm/gc/,这个我没用过,因为据说不实用:)故本文不讨论。
3)这是从QT中学到的方法,将堆中分配的内存组成森林,然后删除根结点的时候级联删除所有子结点。这样就简化了内存管理:从管理所有内存到只要管理根结点。
先讨论方法1:
对于智能指针来说,如果存在互相引用现象,则会导致内存无法释放,所以使用时务必要规避这种情况。有人不用智能指针而是自己为分配的内存管理引用计数,这有点类似于重新造轮子,很多情况下是不必要的。
避免发生互相引用并不那么困难,只要遵守这两条原则:
1)如果可以把两个类理解成"整体-部分"关系(如一个是容器,一个是元素),则整体类对象可以持有部分类对象的智能指针,部分类对象不能持有整体类对象的智能指针。整体类对象的生命周期必须真包含部分类对象的生命周期。如果部分类对象中需要保存整体类对象的引用,可以使用原生指针(这是安全的,因为整体类活得久)、引用(推荐,因为使用方便且更安全,用过就知道)或者weak_ptr(比较安全但效率没那么高)。
2)除此之外,任何对象不持有其他对象的智能指针,与其他对象协作的唯一方式是通过参数传入其他对象的智能指针。
但知易行难,没办法检查编程时是否遵守了这两条原则。
再讨论方法3:
这种方法的优点是实现简单,逻辑清晰,效率高。缺点是对异步和closure的支持不好——鬼知道实际调用的时候,指针指向的那货是否还健在。加入引用计数?哇靠,那还不如直接用智能指针。
但是当程序中不存在异步调用和closure要求的时候,不失为一种可行的内存管理方法——大部分客户端和基于阻塞IO的服务端一般满足该条件。即使有异步调用,只要保证要用到的东东在实际调用前不会被干掉(比如那些东东是永存的),也可以使用该方法。
实际编程中如果想要使用该方法,需要基础类库的支持。不然一个个去写基础类库(如vector)的wrapper,实在很繁琐。所以这个从QT中学到的方法,基本上也只能在QT中用了。
下面给出方法3的一种简易实现,CascadeElem类是关键(爆简单),其他都是示例代码哈。
=================================代码的分割线=============================================
#include <iostream>
#include <vector>
using namespace std;
class CascadeElem
{
public:
CascadeElem(CascadeElem* parent)
{
if (parent != NULL)
{
parent->children_elem_.push_back(this);
}
}
virtual ~CascadeElem()
{
std::size_t cnt =
children_elem_.size();
for
(std::size_t i = 0; i
< cnt; ++i)
{
delete
children_elem_[i];
}
}
private:
std::vector<CascadeElem*>
children_elem_;
};
class Child : public CascadeElem
{
public:
Child(CascadeElem* parent, int num)
:
CascadeElem(parent)
, num_(num)
{
}
~Child()
{
cout
<< "~Child("
<< num_
<< ")"
<< endl;
}
private:
int num_;
};
class Foo : public CascadeElem
{
public:
Foo(CascadeElem* parent, int num)
:
CascadeElem(parent)
, num_(num)
{
for (int i = 0; i
< num; ++ i)
{
new
Child(this, i);
}
}
~Foo()
{
cout
<< "~Foo("
<< num_
<< ")"
<< endl;
}
private:
int num_;
};
int main(int argc, char* argv[])
{
Foo* root = new Foo(NULL, 0);
Foo* ptr1 = new Foo(root, 1);
Foo* ptr2 = new Foo(ptr1, 2);
Foo* ptr3 = new Foo(ptr2, 3);
Foo* ptr4 = new Foo(root, 4);
delete root;
return 0;
}
=================================结果的分割线=============================================
~Foo(0)
~Foo(1)
~Child(0)
~Foo(2)
~Child(0)
~Child(1)
~Foo(3)
~Child(0)
~Child(1)
~Child(2)
~Foo(4)
~Child(0)
~Child(1)
~Child(2)
~Child(3)
可见所有new的对象都被级联删除了。