C++中没有垃圾回收,所有的内存都要自己管理,也就是说new 与 delete必须由程序员自己去一一对应起来。这个难免会出现这样那样的内存问题。在这种背景下,智能指针应运而生。
智能指针的最终的目的是将一块内存交给一个托管对象,内存何时释放由托管对象来决定,而对对象的访问也使用托管对象来实现。这样子对象new完后就不需要显示的delete了,在它的生命周期结束后(也就是没有托管对象引用它的情况下)就可以自动被释放掉了。
智能指针有些比较简单,如 std::auto_ptr , boost::scope_ptr等等,它们的功能都比较类似,就是在某一个区域内指向一个new出来的对象,当这个指针的生命周期结束后,在析构函数中调用delete函数释放这块内存,这类智能指针充其量就是少写了一个delete而已,由于它不能被传递到该指针生命周期外的其它的地方使用,故没什么太大的用处。
稍微复杂一些的智能指针都是维护是引用记数器的智能指针,这种指针可以任意传递,用法基本上和原生指针类似,所以使用的比较多一些。
带引用记数的智能指针原理上基本类似,就是new了某一个资源的时候,给该资源加上一个计数器,每当有新的智能指针指向这个资源的时候,计数器就加1,当某个指向该资源智能指针超出生命周期或由于其它原因解除了和该资源的引用关系时,计数器减1,当计数器减到0 的时候,表示该资源已经没有指针引用,就可以被释放了。
根据记数器所处的位置不同,基本引用记数器的智能指针又分为两类,一类是计数器处于资源中,一类是记数器处于智能指针中。
引用计数器处于资源中的智能指针管理的资源必须从某一个基类继承下来,在这个基类中提供了addref 与 release方法来管理引用计数。这样子智能指针通过调用资源对象的addref 与 release 方法来实现资源的计数管理。很多类库或系统的框架都会让所有的对象从一个基类中继承,而引用计数功能就可以放在这个基类中实现。这种智能指针是专用的,也就是说它不能管理不从这个基类继承的对象。
另外一种方法是将引用记数器放在智能指针本身中,这样子智能指针就可以管理任意new出来的对象的生命周期,而不会对象本身作任何限制。这类智能指针的基本原理是每当一个智能指针被一个原生指针初始化的时候,就new出来一个引用记数器。当指针被传递的时候,这个引用记数器中的值被修改,而引用记数器本身的地址也被传递给其它的智能指针,这种技术的关键在于,智能指针中保存的不同引用计数器本身,而是引用计数器的一个地址,这样就可以实现同一个资源的多个智能指针的传递过程中能保证引用记数的正确。最后,智能指针引用的对象的释放与引用记数器本身的释放是同步的,即一个资源的引用对象为0的时候,它就可以被删除了,同时,这个引用对象也可以被删除了,因为一个引用对象就是绑定到一个特写资源的,引用对象本身不对重用。这种类型的智能指针代表是boost::share_ptr。
基于引用计数的智能指针一般都会存在循引用的问题,关于循环引用这里兴个例子(代码是网上拷的,见谅)
#include <string>#include <iostream>#include <boost/shared_ptr.hpp>#include <boost/weak_ptr.hpp>class parent;class children;typedef boost::shared_ptr<parent> parent_ptr;typedef boost::shared_ptr<children> children_ptr;class parent{ public: ~parent() { std::cout <<"destroying parent/n"; }public: children_ptr children;};class children{ public: ~children() { std::cout <<"destroying children/n"; }public: parent_ptr parent;};void test(){ parent_ptr father(new parent()); children_ptr son(new children); father->children = son; son->parent = father;}void main(){ std::cout<<"begin test.../n"; test(); std::cout<<"end test./n";}
运行该程序可以看到,即使退出了test函数后,由于parent和children对象互相引用,它们的引用计数都是1,不能自动释放,并且此时这两个对象再无法访问到。这就引起了c++中那臭名昭著的内存泄漏。
为了解决这种情况,就出现了另一个概念,叫弱引用,弱引用指针从一个强引用指针产生,弱引用不增加被引用对象的引用计数,并提供了相应的函数来判断当前引用的对象是否已经过期被释放。如boost::weak_ptr为是一个指向boost::share_ptr的弱引用。