auto_ptr、shared_ptr、weak_ptr、scoped_ptr用法小结

auto_ptr
 
auto_ptr是现在标准库里面一个轻量级的智能指针的实现,存在于头文件 memory中,之所以说它是轻量级,是因为它只有一个成员变量(拥有对象的指针),相关的调用开销也非常小。
 

下面的代码来自于VC++ 8.0里面的源码:

  auto_ptr、shared_ptr、weak_ptr、scoped_ptr用法小结_第1张图片
 
里面有个auto_ptr_ref的数据结构,我们可以把它忽略,这个只是内部使用的代理结构,用于一些隐式的const变化,我们客户端代码通常不会直接使用到它。
 
我们可以看到除了构造函数,拷贝构造函数,赋值函数,析构函数和两个重载操作符(*,->)外,还有get,release和reset三个函数,它们的作用分别是:
 
1. get,获得内部对象的指针
2. release,放弃内部对象的所有权,将内部指针置为空
3. reset,销毁内部对象并接受新的对象的所有权(如果使用缺省参数的话,也就是没有任何对象的所有权)
 

下面的例程来自Exceptional C++,Item 37:

 
//  Example 2: Using an auto_ptr
//
void  g()
{
     T
* pt1 = new T;
 
    // right now, we own the allocated object
 
    // pass ownership to an auto_ptr
     auto_ptr<T> pt2( pt1 );
 
    // use the auto_ptr the same way
 
    // we'd use a simple pointer
     *pt2 = 12;       // same as "*pt1 = 12;"

     pt2
->SomeFunc(); // same as "pt1->SomeFunc();"
     
// use get() to see the pointer value
     assert( pt1 == pt2.get() );
     
// use release() to take back ownership
     T* pt3 = pt2.release();
     
// delete the object ourselves, since now
     
// no auto_ptr owns it any more
     delete pt3;

}
  //  pt2 doesn't own any pointer, and so won't
 
//  try to delete it... OK, no double delete
 

//  Example 3: Using reset()
//
void  h()
{
     auto_ptr
<T> pt( new T(1) );
     pt.reset( 
new T(2) );
    
// deletes the first T that was
    
// allocated with "new T(1)"

}
  //  finally, pt goes out of scope and
 
//  the second T is also deleted

 

 
 
从上面的例子来看,auto_ptr的使用很简单,通过构造函数拥有一个动态分配对象的所有权,然后就可以被当作对象指针来使用,当auto_ptr对象被销毁的时候,它也会自动销毁自己拥有所有权的对象(嗯,标准的RAAI做法),release可以用来手动放弃所有权,reset可用于手动销毁内部对象。
 
但实际上,auto_ptr是一个相当容易被误用并且在实际中常常被误用的类。原因是由于它的对象所有权占用的特性和它非平凡的拷贝行为。
 
auto_ptr的对象所有权是独占性的!
 
这决定了不可能有两个auto_ptr对象同时拥有同一动态对象的所有权,从而也导致了auto_ptr的拷贝行为是非对等的,其中伴随着对象所有权的转移。
 
我们仔细观察auto_ptr的源码就会发现拷贝构造和赋值操作符所接受的参数类型都是非const的引用类型( auto_ptr <_Ty>& ),而不是我们一般应该使用的const引用类型,查看源码我们会发现:
 
         auto_ptr(auto_ptr < _Ty >&  _Right) _THROW0()
                 : _Myptr(_Right.release())

                 
{        // construct by assuming pointer from _Right auto_ptr
                 }

 
         template
< class  _Other >
                 auto_ptr
< _Ty >&   operator = (auto_ptr < _Other >&  _Right) _THROW0()

                 
{        // assign compatible _Right (assume pointer)
                 reset(_Right.release());
                 
return (*this);
                 }

 

 
拷贝过程中被拷贝的对象(_Right)都会被调用release来放弃所包括的动态对象的所有权,动态对象的所有权被转移了,新的auto_ptr独占了动态对象的所有权。也就是说被拷贝对象在拷贝过程中会被修改,拷贝物与被拷贝物之间是非等价的。这意味着如下的代码是错误的(例程来自 Exceptional C++ Item 37):
 
//  Example 6: Never try to do work through

//             a non-owning auto_ptr
//
void  f()
{
     auto_ptr
<T> pt1( new T );
     auto_ptr
<T> pt2;

     pt2 
= pt1; // now pt2 owns the pointer, and

              
// pt1 does not
     pt1->DoSomething();

              
// error: following a null pointer
}

 

 
同时也不要将auto_ptr放进标准库的容器中,否则在标准库容器无准备的拷贝行为中(标准库容器需要的拷贝行为是等价的),会导致难以发觉的错误。
auto_ptr特殊的拷贝行为使得使用它来远距离传递动态对象变成了一件十分危险的行为,在传递的过程中,一不小心就会留下一些实际为空但程序本身却缺少这样认知的auto_ptr对象。
 
简单的说来,auto_ptr适合用来管理生命周期比较短或者不会被远距离传递的动态对象,使用auto_ptr来管理动态分配对象,最好是局限于某个函数内部或者是某个类的内部。也就是说,动态对象的产生,使用和销毁的全过程是处于一个小的受控的范围,而不会在其中加入一些适应未来时态的扩展。


auto_ptr的几点注意事项:
1、auto_ptr不能共享所有权
2、auto_ptr不能指向数组
3、auto_ptr不能作为容器的成员
4、不能通过复制操作来初始化auto_ptr
std::auto_ptr p(new int(42)); //OK
std::atuo_ptrp = new int(42);//Error
这是因为auto_ptr的构造函数被定义了explicit
5、不要把auto_ptr放入容器


shared_ptr

shared_ptr是Boost库所提供的一个智能指针的实现,正如其名字所蕴意的一样:
 

An important goal of shared_ptr is to provide a standard shared-ownership pointer.

shared_ptr的一个重要目的就是为了提供一个标准的共享所有权的智能指针。
                                                        —— Boost库文档
 
没错,shared_ptr就是为了解决auto_ptr在对象所有权上的局限性(auto_ptr是独占的),在使用引用计数的机制上提供了可以共享所有权的智能指针,当然这不会没有任何额外的代价……
 
首先一个shared_ptr对象除了包括一个所拥有对象的指针(px)外,还必须包括一个引用计数代理对象( shared_count)的指针(pn)。而这个引用计数代理对象包括一个真正的多态的引用计数对象( sp_counted_base)的指针(_pi),真正的引用计数对象在使用VC编译器的情况下包括一个虚表,一个虚表指针,和两个计数器。
 
下图中result是一个shared_ptr对象,我们可以清楚看到它展开后所包含的数据:
 

auto_ptr、shared_ptr、weak_ptr、scoped_ptr用法小结_第2张图片

 
假设我们有多个(5个以上)shared_ptr共享一个动态对象,那么每个shared_ptr的开销比起只使用原生指针的开销大概在3,4倍左右(这还是理想状况,忽略了动态分配带来的俑余开销)。如果只有一个shared_ptr独占动态对象,空间上开销更是高度十数倍!而auto_ptr的开销只是使用原生指针的两倍。
 
时间上的开销主要在初始化和拷贝操作上,*和->操作符重载的开销跟auto_ptr是一样的。
 
当然开销并不是我们不使用shared_ptr的理由,永远不要进行不成熟的优化,直到性能分析器告诉你这一点,这是Hurb提出的明智的建议。以上的说明只是为了让你了解强大的功能背后总是伴随着更多的开销,shared_ptr应该被使用,但是也不要过于滥用,特别是在一些auto_ptr更擅长的地方。
 
下面是shared_ptr的类型定义:
 
 template < class  T >   class  shared_ptr  {
 

    
public:
 

      typedef T element_type;
 

      shared_ptr(); 
// never throws

      template
<class Y> explicit shared_ptr(Y * p);

      template
<class Y, class D> shared_ptr(Y * p, D d);

      
~shared_ptr(); // never throws
 

      shared_ptr(shared_ptr 
const & r); // never throws

      template
<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws

      template
<class Y> explicit shared_ptr(weak_ptr<Y> const & r);

      template
<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);
 

      shared_ptr 
& operator=(shared_ptr const & r); // never throws 

      template
<class Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws

      template
<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
 

      
void reset(); // never throws

      template
<class Y> void reset(Y * p);

      template
<class Y, class D> void reset(Y * p, D d);
 

      T 
& operator*() const// never throws

      T 
* operator->() const// never throws

      T 
* get() const// never throws
 

      
bool unique() const// never throws

      
long use_count() const// never throws
 

      
operator unspecified-bool-type() const// never throws
 

      
void swap(shared_ptr & b); // never throws
 }
;

 
大多数成员函数都跟auto_ptr类似,但是没有了release(请看注释),reset用来放弃所拥有对象的所有权或拥有对象的变更,会引起原有对象的引用计数的减少。

 
Note:
 
Boost文档里面的QA说明了为什么不提供release函数
 

Q. Why doesn
' t shared_ptr provide a release() function?

A. shared_ptr cannot give away ownership unless it
' s unique() because the other copy will still destroy
 the object.

Consider:
shared_ptr
< int >  a( new   int );

shared_ptr
< int >  b(a);  //  a.use_count() == b.use_count() == 2
 
int   *  p  =  a.release();
 

//  Who owns p now? b will still call delete on it in its destructor.
Furthermore, the pointer returned by release() would be difficult to deallocate reliably, 
as  the source shared_ptr could have been created with a custom deleter.

 
use_count返回引用计数的个数,unique拥于确认是否是独占所有权(use_count为1),swap用于交换两个shared_ptr对象(即交换所拥有的对象),有一个bool类型转换操作符使得shared_ptr可用于需要的bool类型的语境下,比如我们通常用if(pointer)来判断某个指针是否为空。
 
Boost库里面有很多shared_ptr的使用例程,文档里面也列举了许许多多shared_ptr的用途,其中最有用也最常用的莫过于传递动态分配对象,有了引用计数机制,我们现在可以安全地将动态分配的对象包裹在shared_ptr里面跨越模块,跨越线程的边界传递。shared_ptr为我们自动管理对象的生命周期,嗯,C++也可以体会到Java里面使用引用的美妙之处了。
 

另外,还记得Effective C++里面(或者其它的C++书籍),Scott Meyer告诉你的:在一个由多个模块组成的系统里面,一个模块不用试图自己去释放另外一个模块分配的资源,而应该遵循谁分配谁释放的原则。正确的原则但是有时难免有时让人忽略(过于繁琐),将资源包装在shared_ptr里面传递,而shared_ptr保证了在资源不再被拥有的时候,产生资源的模块的delete语句会被调用。

 
shared_ptr是可以拷贝和赋值的,拷贝行为也是等价的,并且可以被比较,这意味这它可被放入标准库的一般容器(vector,list)和关联容器中(map)。
 
shared_ptr可以用来容纳多态对象,比如所下面的例子:
 
class  Base
{
}


class  Derived :  public  Base
{
}

 

shared_ptr
< Base >  sp_base( new  Derived);

 
 
甚至shared_ptr也具备多态的行为:
 
Derived *  pd  =   new  Derived;
 
shared_ptr
< Derived >  sp_derived(pd);
shared_ptr
< Base >  sp_base2(sp_derived);
 
上面的语句是合法的,shared_ptr会完成所需的类型转换,当shared_ptr的模版参数Base的确是Derived的基类的时候。
 
 
最后是一个小小的提醒,无论是使用auto_ptr还是shared_ptr,都永远不要写这样的代码:
 
A *  pa  =   new  A;
xxx_ptr
< A >  ptr_a_1(pa);
xxx_ptr
< A >  ptr_a_2(pa);

 
很明显,在ptr_a_1和ptr_a_2生命周期结束的时候都会去删除pa,pa被删除了两次,这肯定会引起你程序的崩溃。

可能有人会疑问,假如使用shared_ptr,第二次引用pa的时候,计数会加一。知道ptr_a_1和ptr_a_2的生命周期全部结束时才会删除,怎么会崩溃呢。

实际上,shared_ptr的构造函数是不一样的

template  explicit shared_ptr(Y* p);

这个构造函数获得给定指针p的所有权。参数 p 必须是指向Y 的有效指针。构造后引用计数设为1。唯一从这个构造函数抛出的异常是std::bad_alloc (仅在一种很罕见的情况下发生,即不能获得引用计数器所需的自由空间)。



shared_ptr(const shared_ptr& r);

r中保存的资源被新构造的shared_ptr所共享,引用计数加一。这个构造函数不会抛出异常。


因此,直接用指针作为构造形参传入shared_ptr,计数是不会加一的,当它生命周期结束时,就会去删除指针,因此,上面的例子实际上删除了两次。

当然,这个误用的例子比较明显,但是在某种情况下,可能会一不小心就写出如下的代码(嗯,我承认我的确做过这样的事情):
 
void  DoSomething(xxx_ptr < A > )
{

         
//do something
}

 
class  A
{
         doSomething()
         
{

                 xxx_ptr
<A> ptr_a(this);
                 DoSomething(ptr_a);
         }

}
;
 
int  main()
{

         A a;
         a.doSomething();

         
//continue do something with a, but it was already destory
}

 
在函数a.doSomething()里面发生了什么事情,它为了调用DoSomething所以不得不把自己包装成一个xxx_ptr,但是忘记在函数结束的时候,xxx ptr_a被销毁的同时也销毁了自己,程序或者立刻崩溃或者在下面的某个时间点上崩溃!
 
所以你在使用智能指针做为函数参数的时候请小心这样的误用,有时候使用智能指针作为函数参数不一定是一个好注意。比如请遵循下面的建议,请不要在属于类型A的接口的一部分的非成员函数或者跟A有紧密联系的辅助函数里面使用xxx_ptr作为函数的参数类型。



weak_ptr


weak_ptr是为配合shared_ptr而引入的一种智能指针来协助shared_ptr工作,它可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起引用记数的增加或减少。没有重载*和->但可以使用lock获得一个可用的shared_ptr对象
C++代码  复制代码  收藏代码
  1. template<class T> class weak_ptr   
  2. {   
  3. public:   
  4.     weak_ptr();   
  5.   
  6.     template<class Y> weak_ptr(shared_ptr const & r);   
  7.     weak_ptr(weak_ptr const & r);   
  8.   
  9.     ~weak_ptr();   
  10.     weak_ptr & operator=(weak_ptr const & r);   
  11.   
  12.     long use_count()const;   
  13.     //判断是否过期   
  14.     bool expired()const;   
  15.     //得到一个空的或者被协助的shared_ptr   
  16.     shared_ptr lock()const;   
  17.   
  18.     void reset();   
  19.     void swap(weak_ptr & b);   
  20. };  
[c++]  view plain copy
  1. template<class T> class weak_ptr  
  2. {  
  3. public:  
  4.     weak_ptr();  
  5.   
  6.     template<class Y> weak_ptr(shared_ptr const & r);  
  7.     weak_ptr(weak_ptr const & r);  
  8.   
  9.     ~weak_ptr();  
  10.     weak_ptr & operator=(weak_ptr const & r);  
  11.   
  12.     long use_count()const;  
  13.     //判断是否过期  
  14.     bool expired()const;  
  15.     //得到一个空的或者被协助的shared_ptr  
  16.     shared_ptr lock()const;  
  17.   
  18.     void reset();  
  19.     void swap(weak_ptr & b);  
  20. };  


C++代码  复制代码  收藏代码
  1. #include   
  2. #include   
  3. #include   
  4. using namespace std;   
  5. using namespace boost;   
  6.   
  7. int main()   
  8. {   
  9.     shared_ptr<int> p(new int(10));   
  10.     weak_ptr<int> w(p);   
  11.     while(!w.expired()){   
  12.         cout << w.use_count() << endl;   
  13.         shared_ptr<int> t = w.lock();   
  14.         cout << *t << endl;   
  15.         cout << w.use_count() << endl;   
  16.         if(w.use_count()==2){   
  17.             break;   
  18.         }   
  19.     }   
  20.     w.reset();   
  21.     cout << w.expired() << endl;   
  22. }   
  23.   
  24. 1   
  25. 10   
  26. 2   
  27. 1  
[c++]  view plain copy
  1. #include  
  2. #include  
  3. #include  
  4. using namespace std;  
  5. using namespace boost;  
  6.   
  7. int main()  
  8. {  
  9.     shared_ptr<int> p(new int(10));  
  10.     weak_ptr<int> w(p);  
  11.     while(!w.expired()){  
  12.         cout << w.use_count() << endl;  
  13.         shared_ptr<int> t = w.lock();  
  14.         cout << *t << endl;  
  15.         cout << w.use_count() << endl;  
  16.         if(w.use_count()==2){  
  17.             break;  
  18.         }  
  19.     }  
  20.     w.reset();  
  21.     cout << w.expired() << endl;  
  22. }  
  23.   
  24. 1  
  25. 10  
  26. 2  
  27. 1  


weak_ptr的一个重要用途是通过lock获得this指针的shared_ptr,使对象自己能够生产shared_ptr来管理自己,但助手类enable_shared_from_this的shared_from_this会返回this的shared_ptr,只需要让想被shared_ptr管理的类从它继承即可

C++代码  复制代码  收藏代码
  1. #include   
  2. #include   
  3. #include   
  4. using namespace boost;   
  5. using namespace std;   
  6.   
  7. class testWeak:public enable_shared_from_this{//  
  8. public:   
  9.     int i;   
  10.     testWeak(int ii):i(ii){}   
  11.     void print(){   
  12.         cout << i << endl;   
  13.     }   
  14. };   
  15.   
  16. int main()   
  17. {   
  18.     shared_ptr sp = make_shared(100);   
  19.     shared_ptr tw = sp->shared_from_this();   
  20.     tw->print();   
  21. }   

循环引用

 

引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引用的对象。一个简单的例子如下:

 

Cpp代码  复制代码  收藏代码
  1. #include    
  2. #include    
  3. #include    
  4. #include    
  5.   
  6. class parent;   
  7. class children;   
  8.   
  9. typedef boost::shared_ptr parent_ptr;   
  10. typedef boost::shared_ptr children_ptr;   
  11.   
  12. class parent   
  13. {   
  14. public:   
  15.     ~parent() { std::cout <<"destroying parent\n"; }   
  16.   
  17. public:   
  18.     children_ptr children;   
  19. };   
  20.   
  21. class children   
  22. {   
  23. public:   
  24.     ~children() { std::cout <<"destroying children\n"; }   
  25.   
  26. public:   
  27.     parent_ptr parent;   
  28. };   
  29.   
  30.   
  31. void test()   
  32. {   
  33.     parent_ptr father(new parent());   
  34.     children_ptr son(new children);   
  35.   
  36.     father->children = son;   
  37.     son->parent = father;   
  38. }   
  39.   
  40. void main()   
  41. {   
  42.     std::cout<<"begin test...\n";   
  43.     test();   
  44.     std::cout<<"end test.\n";   
  45. }  
[cpp]  view plain copy
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5.   
  6. class parent;  
  7. class children;  
  8.   
  9. typedef boost::shared_ptr parent_ptr;  
  10. typedef boost::shared_ptr children_ptr;  
  11.   
  12. class parent  
  13. {  
  14. public:  
  15.     ~parent() { std::cout <<"destroying parent\n"; }  
  16.   
  17. public:  
  18.     children_ptr children;  
  19. };  
  20.   
  21. class children  
  22. {  
  23. public:  
  24.     ~children() { std::cout <<"destroying children\n"; }  
  25.   
  26. public:  
  27.     parent_ptr parent;  
  28. };  
  29.   
  30.   
  31. void test()  
  32. {  
  33.     parent_ptr father(new parent());  
  34.     children_ptr son(new children);  
  35.   
  36.     father->children = son;  
  37.     son->parent = father;  
  38. }  
  39.   
  40. void main()  
  41. {  
  42.     std::cout<<"begin test...\n";  
  43.     test();  
  44.     std::cout<<"end test.\n";  
  45. }  

 

    运行该程序可以看到,即使退出了test函数后,由于parent和children对象互相引用,它们的引用计数都是1,不能自动释放,并且此时这两个对象再无法访问到。这就引起了c++中那臭名昭著的内存泄漏。

 

一般来讲,解除这种循环引用有下面有三种可行的方法:

  • 当只剩下最后一个引用的时候需要手动打破循环引用释放对象。
  • 当parent的生存期超过children的生存期的时候,children改为使用一个普通指针指向parent。
  • 使用弱引用的智能指针打破这种循环引用。

虽然这三种方法都可行,但方法1和方法2都需要程序员手动控制,麻烦且容易出错。这里主要介绍一下第三种方法和boost中的弱引用的智能指针boost::weak_ptr。

 

 

强引用和弱引用

 

对于强引用来说,如果被引用的对象还活着,那么这个强引用也存在(就是说,当至少有一个强引用,那么这个对象就不能被释放)。boost::share_ptr就是强引用。

 

相对而言,对于弱引用来说,当引用的对象活着的时候弱引用不一定存在。仅仅是当它存在的时候的一个引用。弱引用并不修改该对象的引用计数,这意味这弱引用它并不对对象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。

 

 

boost::weak_ptr

 

boost::weak_ptr是boost提供的一个弱引用的智能指针,它的声明可以简化如下:

 

Cpp代码  复制代码  收藏代码
  1. namespace boost {   
  2.   
  3.     template<typename T> class weak_ptr {   
  4.     public:   
  5.         template <typename Y>   
  6.         weak_ptr(const shared_ptr& r);   
  7.   
  8.         weak_ptr(const weak_ptr& r);   
  9.   
  10.         ~weak_ptr();   
  11.   
  12.         T* get() const;    
  13.         bool expired() const;    
  14.         shared_ptr lock() const;   
  15.     };    
  16. }  
[cpp]  view plain copy
  1. namespace boost {  
  2.   
  3.     template<typename T> class weak_ptr {  
  4.     public:  
  5.         template <typename Y>  
  6.         weak_ptr(const shared_ptr& r);  
  7.   
  8.         weak_ptr(const weak_ptr& r);  
  9.   
  10.         ~weak_ptr();  
  11.   
  12.         T* get() const;   
  13.         bool expired() const;   
  14.         shared_ptr lock() const;  
  15.     };   
  16. }  

 

    可以看到,boost::weak_ptr必须从一个boost::shared_ptr或另一个boost::weak_ptr转换而来。这也说明,进行该对象的内存管理的是那个强引用的boost::shared_ptr。boost::weak_ptr只是提供了对管理对象的一个访问手段。

 

boost::weak_ptr除了对所管理对象的基本访问功能(通过get()函数)外,还有两个常用的功能函数:expired()用于检测所管理的对象是否已经释放;lock()用于获取所管理的对象的强引用指针。

 

 

通过boost::weak_ptr来打破循环引用

 

由于弱引用不更改引用计数,类似普通指针,只要把循环引用的一方使用弱引用,即可解除循环引用。对于上面的那个例子来说,只要把children的定义改为如下方式,即可解除循环引用:

 

Cpp代码  复制代码  收藏代码
  1. class children   
  2. {   
  3. public:   
  4.     ~children() { std::cout <<"destroying children\n"; }   
  5.   
  6. public:   
  7.     boost::weak_ptr parent;   
  8. };  
[cpp]  view plain copy
  1. class children  
  2. {  
  3. public:  
  4.     ~children() { std::cout <<"destroying children\n"; }  
  5.   
  6. public:  
  7.     boost::weak_ptr parent;  
  8. };  

 

   最后值得一提的是,虽然通过弱引用指针可以有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的情况下才能使用,也可以是说这个仅仅是一种编译期的解决方案,如果程序在运行过程中出现了循环引用,还是会造成内存泄漏的。因此,不要认为只要使用了智能指针便能杜绝内存泄漏。毕竟,对于C++来说,由于没有垃圾回收机制,内存泄漏对每一个程序员来说都是一个非常头痛的问题。


scoped_ptr


头文件: "boost/scoped_ptr.hpp"

boost::scoped_ptr 用于确保动态分配的对象能够被正确地删除。scoped_ptr 有着与std::auto_ptr类似的特性,而最大的区别在于它不能转让所有权而auto_ptr可以。事实上,scoped_ptr永远不能被复制或被赋值scoped_ptr 拥有它所指向的资源的所有权,并永远不会放弃这个所有权。scoped_ptr的这种特性提升了我们的代码的表现,我们可以根据需要选择最合适的智能指针(scoped_ptr 或 auto_ptr)。

要决定使用std::auto_ptr还是boost::scoped_ptr, 就要考虑转移所有权是不是你想要的智能指针的一个特性。如果不是,就用scoped_ptr. 它是一种轻量级的智能指针;使用它不会使你的程序变大或变慢。它只会让你的代码更安全,更好维护。

下面是scoped_ptr的摘要,以及其成员的简要描述:

namespace boost {

  template class scoped_ptr : noncopyable {
  public:
    explicit scoped_ptr(T* p = 0); 
    ~scoped_ptr(); 

    void reset(T* p = 0); 

    T& operator*() const; 
    T* operator->() const; 
    T* get() const; 
   
    void swap(scoped_ptr& b); 
  };

  template 
    void swap(scoped_ptr & a, scoped_ptr & b); 
}

成员函数

explicit scoped_ptr(T* p=0)

构造函数,存储p的一份拷贝。注意,p 必须是用operator new分配的,或者是null. 在构造的时候,不要求T必须是一个完整的类型。当指针p是调用某个分配函数的结果而不是直接调用new得到的时候很有用:因为这个类型不必是完整的,只需要类型T的一个前向声明就可以了。这个构造函数不会抛出异常。

~scoped_ptr()

删除被指物。类型T在被销毁时必须是一个完整的类型。如果scoped_ptr在它被析构时并没有保存资源,它就什么都不做。这个析构函数不会抛出异常。

void reset(T* p=0);

重置一个 scoped_ptr 就是删除它已保存的指针,如果它有的话,并重新保存 p. 通常,资源的生存期管理应该完全由scoped_ptr自己处理,但是在极少数时候,资源需要在scoped_ptr的析构之前释放,或者scoped_ptr要处理它原有资源之外的另外一个资源。这时,就可以用reset但一定要尽量少用它。(过多地使用它通常表示有设计方面的问题) 这个函数不会抛出异常。

T& operator*() const;

返回一个到被保存指针指向的对象的引用。由于不允许空的引用,所以解引用一个拥有空指针的scoped_ptr将导致未定义行为。如果不能肯定所含指针是否有效,就用函数get替代解引用。这个函数不会抛出异常。

T* operator->() const;

返回保存的指针。如果保存的指针为空,则调用这个函数会导致未定义行为。如果不能肯定指针是否空的,最好使用函数get。这个函数不会抛出异常。

T* get() const;

返回保存的指针。应该小心地使用get,因为它可以直接操作裸指针。但是,get使得你可以测试保存的指针是否为空。这个函数不会抛出异常。get通常在调用那些需要裸指针的函数时使用。

operator unspecified_bool_type() const

返回scoped_ptr是否为非空。返回值的类型是未指明的,但这个类型可被用于Boolean的上下文中。在if语句中最好使用这个类型转换函数,而不要用get去测试scoped_ptr的有效性

void swap(scoped_ptr& b)

交换两个scoped_ptr的内容。这个函数不会抛出异常。

普通函数

template void swap(scoped_ptr& a,scoped_ptr& b)

这个函数提供了交换两个scoped pointer的内容的更好的方法。之所以说它更好,是因为 swap(scoped1,scoped2)可以更广泛地用于很多指针类型,包括裸指针和第三方的智能指针。scoped1.swap(scoped2) 则只能用于它的定义所在的智能指针,而不能用于裸指针。

你可为那些不够智能,没有提供它们自己的交换函数的智能指针创建你的普通swap函数。

用法

scoped_ptr的用法与普通的指针没什么区别;最大的差别在于你不必再记得在指针上调用delete,还有复制是不允许的。典型的指针操作(operator* 和 operator->)都被重载了,并提供了和裸指针一样的语法。用scoped_ptr和用裸指针一样快,也没有大小上的增加,因此它们可以广泛使用。使用boost::scoped_ptr时,包含头文件"boost/scoped_ptr.hpp". 在声明一个scoped_ptr时,用被指物的类型来指定类模板的参数。例如,以下是一个包含std::string指针的scoped_ptr

boost::scoped_ptr p(new std::string("Hello"));

scoped_ptr被销毁时,它对它所拥有的指针调用delete 。


你可能感兴趣的:(c++,技术类)