C++中的资源常常是动态分配内存,如果仅仅分配了,却不还给系统,就会造成内存泄漏。内存是众多资源中之一,其它常见资源还有GDI里的画笔,画刷对象,互斥锁mutex,数据库连接对象,socket等等,不管什么对象,不再使用时都需要返还给系统。
先看一段简单的代码:
A* A::create()
{
A * temp = new A();
return temp;
}
void A::f()
{
A *pt = create();
...<span style="white-space:pre"> </span>//某些其它操作
delete pt;
}
上述代码中的 f 函数,调用了create函数,得到了一个对象指针,最后delete pt 看似没有任何问题,实际上如果省略号部分过早的return ,或者中间抛出了异常,那就接触不到下面的delete语句了,相同的情况在于delete位于一个循环中,但循环中某个部分break 了,那也不会触及delete,这样就造成了内存的泄漏。
注:无论delete如何被略去的,我们泄漏的资源不仅仅是对象的那块内存,还包括对象所保存的任何资源。
我们在代码中,小心谨慎也许可以做到不泄漏,但代码也许会有其他人来维护,他们添加或删除一些return,break之类的,你的代码结构就被改变了,所以为了确保资源总是可以被释放,我们可以使用智能指针,其析构函数自动对其所指对象调用delete,下面就看改进后的写法:
void A::f()
{
auto_ptr<A>pt(create());
//创建一个指针pt,类型为A *,初值为create函数的返回值
....
}
这样,不论控制流如何离开区域块,一旦对象被销毁,其析构函数就会自动被调用,资源被释放。
注:由于auto_ptr 被销毁时会自动删除它所指之物,所以不能让auto_ptr指向同一个对象,不然就会造成一个对象被删除多次以上,产生未定义行为。
缺点:
若通过copy构造函数制,它们会变成NULL,复制所得的指针将获得唯一拥有权
看以下代码:
void A::f()
{
auto_ptr<A>pt(create());
auto_ptr<A>pt2(pt);//pt2指向对象,pt为NULL
pt = pt2;//此时pt2为NULL
}
一系列诡异的复制,意味着auto_ptr还有需要改进的地方,这就引进了一个shared_ptr,他是引用计数型智慧指针,也就是说它会追踪共有多少对象指向某笔资源,并在无人指向时删除该资源。(有点像垃圾回收)
看下面改进代码
void A::f()
{
shared_ptr<A>pt(create());
shared_ptr<A>pt2(pt);//pt,pt2同时指向对象
pt = pt2;//无区别
}
用法与auto_ptr完全相同,且复制如预期一般。所以它们可以用于STL容器以及其他 auto_ptr在复制行为不正确的情况。
最后,再说一个注意点:
不管是auto_ptr还是shared_ptr,它们都只是在析构函数中调用delete p;而不是delete []p; 所以如果是动态分配的数组的话切记不要用智能指针。虽然编译可能可以通过,但实际上还是泄漏了内存。对于数组,用vector,string等几乎完全替代动态分配得的数组。