smart_ptr库是Boost中比较小的库之一,也是实际应用中使用最频繁的库之一。下面结合这段时间对它的学习了解,梳理Boost/smart_ptr库的知识构架。
Boost/smart_ptr库主要目的是为了解决C++中最让人头疼的部分:内存泄漏。自从C++引入异常机制以来,内存泄漏一直围绕着C++程序员,每个C++都小心翼翼地防止着,但内存泄漏就像空气中的NIHI病毒一样,让人防不胜防。如是,各种各样的智能指针开始出现了,最终也就诞生了伟大的Boost/smart库。
经过千万次的锤炼,Boost/smart库已经逐渐完美了,已被C++ 0x接受。估计再向前发展的空间比较小。
Boost/smart库主要构建了以下几种智能指针:
1.scoped_ptr
2.scoped_array
3.shared_ptr
4.shared_array
5.weap_ptr
6.intrusive_ptr
下面分别详细介绍每种smart_ptr的用途。
1. scoped_ptr
scoped_ptr是根据C++标准库中的auto_ptr改进过来的。Auto_ptr的一个很大的缺陷就是在赋值的时候会转移操作权限,而scoped_ptr不会发生这样的情况,原因在于scoped_prt根本就不允许赋值操作:
private:
scoped_ptr(scoped_ptr const &);
scoped_ptr & operator=(scoped_ptr const &);
使用scope_ptr需要注意的地方:
a. scope_ptr只能从new操作符、0以及auto_ptr构造。
b. operator*操作时做好先判断ptr是否为0.如果ptr=0,*操作会导致未定义的行为
使用方法和auto_ptr一样,只是不允许赋值操作。示例:
2 #include < string >
3 #include < boost / scoped_ptr.hpp >
4
5 class A {
6 int i;
7 public:
8 A(int i=0){
9 this->i=i;
10 std::cout<<"A() Called\n";
11 }
12 ~A(){
13 std::cout<<"~A() Called\n";
14 }
15 void f(){
16 std::cout<<"A::f() Called\n";
17 std::cout<<"A::i="<<i<<std::endl;;
18 }
19} ;
20
21 int main() {
22 std::auto_ptr<A> p_auto(new A(6));
23 boost::scoped_ptr<A> p_scope(new A());
24 p_auto->f();
25 p_scope->f();
26 std::auto_ptr<A> p_auto2=p_auto;
27 //boost::scoped_ptr<A> p_scope2=p_scope;//编译错误
28 p_auto->f();//p_auto已经转移操作权限了,返回的是个裸指针
29 p_auto2->f();
30
31 return 0;
32}
2. scoped_array
scoped_array是scoped_ptr的数组形式的版本。scoped_array之所以是一个单独的类而不是scoded_ptr的一个特化,这是因为元编程技术无法区分指向单个对象的指针和指向数组的指针,不管如何努力,还是没有人能够发现一种可靠的能区分这两种形式指针的方法,这是因为数组太容易退化成指针了而且没有任何信息表明它指向数组。最后只能我们用scoped_array单独处理指向数组的指针了,正如delete无法取代delete[]一样。
使用示例:
2 #include < iostream >
3 #include < functional >
4 #include < boost / scoped_array.hpp >
5
6 class A {
7 int i;
8 public:
9 A(int i=0){
10 this->i=i;
11 std::cout<<"A() Called\n";
12 }
13 ~A(){
14 std::cout<<"~A() Called\n";
15 }
16 void f(){
17 std::cout<<"A::f() Called\n";
18 std::cout<<"A::i="<<i<<std::endl;;
19 }
20 void seti(int i){
21 this->i=i;
22 }
23} ;
24
25 int main() {
26 boost::scoped_array<A> p_scope_array(new A[10]);
27 for(int i=0;i<10;i++){
28 p_scope_array[i].seti(i);
29 p_scope_array[i].f();
30 }
31
32 return 0;
33}
3. shared_ptr
就我个人看来,shared_ptr是最有使用价值的智能指针了,它采用了非侵入式的引用技术技术,几乎可以取代平时工作中可能导致内存泄漏的普通指针。另外它还提供了可自定义的析构方法,这么一来,它几乎可以安全管理所有资源了(包含文件,句柄等等)。值得欣慰的是,shared_ptr和shared_array已经被C++标准委员会接受,C++ 0x中应该就会包含这个让大家使用很方便的智能指针了。
shared_ptr解决了共享对象什么时候删除的问题。让C++程序员不用再想尽方法删除共享对象了。
使用shared_ptr需要注意的地方:
a. shared_ptr可以从裸指针、另一个shared_ptr、std::auto_ptr或者weak_ptr构造而来
b. shared_ptr的引用计数器来自堆分配。所以通过裸指针或者std::auto_ptr构造时。可能会因为堆空间不足而抛出std::bad_alloc的异常(非常小的几率)
c. 从只能指针weak_ptr构造shared_ptr使weak_ptr的使用具有了线程安全。这里需要注意的是,如果weak_ptr悬空的话,那么shared_ptr将抛出一个bad_weak_ptr的异常。
d. shared_ptr构造的时候,允许传递释放所存储对象的方法,这就让我们可以用shared_ptr管理除了指针外的其他设备,示例:
2 #include < boost / shared_ptr.hpp >
3
4
5 class FileCloser {
6 public:
7 void operator()(FILE *file){
8 std::cout<<"Called void operator()(FILE *file)"<<std::endl;
9 if(file!=0)
10 fclose(file);
11 }
12} ;
13
14 int main() {
15 {
16 FILE *f=fopen("test.txt","r");
17 if(f==0){
18 std::cout<<"Unable to open file"<<std::endl;
19 }
20 boost::shared_ptr<FILE> p_shared_file(f,FileCloser());
21 //通过p_shared_file操作文件
22 }
23 std::cout<<"File has been closed"<<std::endl;
24
25 return 0;
26}
27
e. shared_ptr可以用在C++标准容器中
f. 最后附上shared_ptr的设计源码,方面查阅
4. shared_array
shared_array是shared_ptr的数组形式,他们的关系就像scoped_ptr和scoped_array的关系。shared_array的接口了shared_ptr的接口非常相似,但shared_array重载了下标运算符{},且不支持自定义的析构方式。
5. weap_ptr
智能指针weap_ptr是shared_ptr的观察者,它不会影响shared_ptr所共享资源的所有权。为什么需要weap_ptr呢?因为在许多情况下,需要观察某个共享资源但是又不想接受它的所有权,例如为了打破循环依赖关系、为了观察某个共享资源而不想接受它的所有权或者为了避免悬空指针时,就需要使用weap_ptr。
暂时有点难懂,留下它的设计文档以后需要的时候再研究。
6. intrusive_ptr
intrusive_ptr是shared_ptr的侵入式版本了,因为使用频率很低,暂时不做说明。
总结:Boost.Smart库是一个非常非常优秀的库,是一个无论怎么评价都不为过的优秀库,值得学习,值得广泛应用。