(一)Boost库中,智能指针并不只shared_ptr一个。同族但有不同的功能目标的还有如下5个:
 scoped_ptr
 scoped_array
 shared_ptr
 shared_array
 weak_ptr
scoped_ptr、scoped_array与标准C++中的智能指针auto_ptr功能基本类似,不过它不传递所有权,不可复制。它不能共享所有权的特性却大大限制了其使用范围,而boost::shared_ptr可以解决这一局限。顾名思义,boost::shared_ptr是可以共享所有权的智能指针,其主要目标就是在小范围,小作用域中使用,以减少显式的delete, new配对操作,提高代码的安全性。scoped_ptr是针对指针的版本,而scoped_array同是专门针对数组的。shared_array、shared_ptr它们在所有对象中,共享所指向实体的所有权。即只要是指向同一个实体对象的shared_ptr对象,都有权操作这个对象,并根据自己产生新的对象,并把所有权共享给新的对象。即它是满足STL对对象的基本要求可复制,可赋值的。可以与所有的STL容器,算法结合使用。顾名思义, shared_ptr是针对任意类型的指针的,而shared_array则是专门针对任意类型的数组的。
boost::shared_ptr的解决方案:
上面我们分析了shared_ptr问题的产生,以及可能的实现方式和应该注意的问题。下面我们来看看boost::shared_ptr是如何实现上面的思想的。
    1.文件结构
   定义boost::shared_ptr主要涉及到以下文件
     shared_ptr.hpp
   detail/shared_count.hpp
   detail/sp_counted_base.hpp
   detail/sp_counted_base_pt.hpp
   detail/sp_counted_base_win32.hpp
   detail/sp_counted_base_nt.hpp
   detail/sp_counted_base_pt.hpp
   detail/sp_counted_base_gcc_x86.hpp
   ...    
   detail/sp_counted_base_impl.hpp
 涉及的类主要有以下几个:
   shared_ptr
   shared_count
   sp_counted_base
   sp_counted_base_impl 
 其中
   shared_ptr定义在shared_ptr.hpp中
   share_count定义在shared_count.hpp中
   sp_counted_base定义在sp_counted_base_XXX.hpp中。
 XXX指代针对特定平台的实现。
   sp_counted_base_impl定义在sp_counted_base_impl.hpp中
  2.类功能
   sp_counted_base 是问题4中,记数器的实现。
 针对不同的平台使用了不同的同步机制。如pt是针对linux, unix平台使用pthread接口进行同步的。
 win32是针对windows平台,使用InterlockedIncrement, InterlockedDecrement机制。
 gcc_x86是针对AMD64硬件平台的,内部使用汇编指令实现了atomic_increment,
 atomic_decrement。
 sp_counted_base_impl是个模板,它继承自sp_counted_base,
 主要实现了父类中一个纯虚函数dispose。具体的由它来负责在记数值到0(即没有代理时)释放所托管的资源。
 shared_count是个类模板。它存在的意义在于和代理类shared_ptr同生共死,
 在构造函数中生成记数器,在代理的传递过程中驱动记数器增减。
   shared_ptr是个类模板。它是托管资源的代理类,所有对资源的操作,
 访问都通过它来完成。
3.类详细介绍
  shared_ptr 类模板。 它是我们直接使用的代理类。 两个属性pn : shared_count   记数器类。 shared_ptr完全操作pn的生命期, 
在构造时构造它,在析构时自动析构它。 
这些都是利用构造与析构函数的特性自动完成的。template<class Y>explicit shared_ptr( Y * p ): px( p ), pn( p ) // Y must be 
complete{detail::sp_enable_shared_from_this( pn, p, p );}px : T*           它是代理的资源的指针。 所有针对资源的操作*, 
->都直接使用它来完成。 
    reference operator* () const // neverthrows
     {
         BOOST_ASSERT(px != 0);
         return *px;
     }
    T * operator-> () const // never throws
     {
         BOOST_ASSERT(px != 0);
         return px;
     }
     两个方法:
     operator=完成代理的赋值传递。 
 通过这样,就可以有多个代理同时托管同一个资源。在众多的代理中, 它们共享所托管资源的操作权。
     
     shared_ptr & operator=(shared_ptrconst & r) // never throws
     {
         px = r.px;
         pn = r.pn; // shared_count::op=doesn't throw
         return *this;
     } 
    T* get()资源原始位置的获取。通过这个方法,我们可以直接访问到资源, 
 并可以对它进行操作。
     
     T * get() const // never throws
     {
         return px;
     }
    user_count返回当前资源的代理个数,即有多少个对些资源的引用。
     
     long use_count() const // neverthrows
     {
         return pn.use_count();
     }
    
 为了方便操作,并完全模拟原生指针的行为,boost::shared_ptr还定义了大量的其它操作函数。
     shared_count类 记数器的包装。
     一个属性:
     pi : sp_counted_base *它指向真正的记数器。
    构造函数:
     template<class Y> explicitshared_count( Y * p ): pi_( 0 )
     #ifdefined(BOOST_SP_ENABLE_DEBUG_HOOKS)
         , id_(shared_count_id)
  #endif
     {
     #ifndef BOOST_NO_EXCEPTIONS
        try
         {
             pi_ = newsp_counted_impl_p<Y>( p );
         }
         catch(...)
         {
             boost::checked_delete( p );
             throw;
         }
#else
pi_ = new sp_counted_impl_p<Y>( p);
        if( pi_ == 0 )
         {
             boost::checked_delete( p );
             boost::throw_exception(std::bad_alloc() );
         }
 #endif
     }
    
 当使用一个资源指针来构造一个shared_count时,它知道针对此资源要生成一个代理。 
 所以生成一个记数器pi。如果在构造记数器的过程中出现任何异常行为,即记数器资源的初始化未成功完成时, 就释放掉资源。 
 (这就是资源申请即初始化, 对于一个资源管理类来说,要不所有资源申请成功, 要不构造失败)
  
 operator=赋值函数。 
 在shared_ptr被赋值的时候,会调用它。经过复制后一个shared_ptr变成两个, 所以要对记数器进行增加。 
 同时如果被赋值的代理原有托管的资源将被释放。
    
     shared_count & operator=(shared_count const & r) // nothrow
     {
         sp_counted_base * tmp = r.pi_;
        if( tmp != pi_ )
         {
             if( tmp != 0 )tmp->add_ref_copy();
             if( pi_ != 0 )pi_->release();
             pi_ = tmp;
        }
        return *this;
     }
    析构函数析构函数在shared_ptr超出作用域被析构时自动调用。 
 每析构一个shared_ptr,则代理数就少一个, 所以调用记数器的release函数, 
 减少记数值。
     
     ~shared_count() // nothrow
     {
         if( pi_ != 0 ) pi_->release();
  #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
         id_ = 0;
  #endif
     }
    sp_counted_base 记数器类。 
 这是一个普通类,没有被模板化。它定义了记数器公用的所有操作, 并实现了同步。
    两个属性
     use_count记录当前的代理数目。
     mutex互斥锁(在不同平台的实现中,是不同的类型。以linux为例, 
 是pthread_mutex_t)
     构造函数初始化互斥锁和记数
     sp_counted_base(): use_count_( 1 ),weak_count_( 1 )
     {
     #if defined(__hpux) &&defined(_DECTHREADS_)
         pthread_mutex_init( &m_,pthread_mutexattr_default );
  #else
         pthread_mutex_init( &m_, 0 );
  #endif
     }
    析构函数释放互斥锁
     virtual ~sp_counted_base() // nothrow
     {
         pthread_mutex_destroy( &m_ );
     }
  
 同步增加记数值,即根据原有的复制构造出或通过赋值产生新的代理时记数加1 
     void add_ref_copy()
     {
         pthread_mutex_lock( &m_ );
         ++use_count_;
         pthread_mutex_unlock( &m_ );
     }
    释放函数,在代理超界被析构时使用。 它首先减少引用记数, 
 然后查看记数值,如果为0, 则调用dispose释放资源。并销毁掉本身
     void release() // nothrow
     {
         pthread_mutex_lock( &m_ );
         long new_use_count =--use_count_;
         pthread_mutex_unlock( &m_ );
        if( new_use_count == 0 )
         {
             dispose();
             weak_release();
         }
     }
     
     释放托管资源的函数。 为纯虚函数, 要求在子类中实现。 
 只所以不同这个类中实现,是由于要托管的类型是未知的,如果要实现则要记录这个资源,则此类不得不模板化。 
 将此功能分离到子类中的好处就非常明显。子类可以直接利用父类的功能,只要模板化一下,实现一个函数就行。 
 这样会大大加快编译速度。
     
  virtual void dispose() = 0; // nothrow
 销毁函数。 在shared_count中的pi即记数器是New出来的。 
 所以在适当的时候要销毁掉。 而这个时机就是资源被释放的时候。 
 即记数器完成了对资源的管理, 同时完成了对自身的管理。
  
     // destroy() is called whenweak_count_ drops to zero.
     virtual void destroy() // nothrow
     {
         delete this;
     }
    
     sp_counted_base_impl 类模板。 
     一个属性
px: T*记录各种类型的资源。
     
     实现了父类的dispose函数。 实现对资源的释放。
     virtual void dispose() // nothrow
     {
     #ifdefined(BOOST_SP_ENABLE_DEBUG_HOOKS)
         boost::sp_scalar_destructor_hook(px_, sizeof(X), this );
  #endif
         boost::checked_delete( px_ );
     }
工作流程:
    1用户申请一个资源p
       p = new Something();
     
     2 用户将资源p托管给shared_ptr;
       shared_ptr(p);
     
     2.1 shared_ptr 构造函数调用,构造出记数对象shared_count
      shared_ptr() : px(p), pn(p)
      
     2.1.1 shared_count 构造函数调用,构造出户数器对象
         pi = newsp_counted_base_impl(p);     
首先让我们通过一个例子看看shared_ptr的基本用法:
#include <string>
 #include <iostream>
 #include <boost/shared_ptr.hpp>
 
 class implementation
 {
 public:
     ~implementation() { std::cout <<"destroyingimplementation\n"; }
     void do_something(){ std::cout << "did something\n"; }
 };
 
 void test()
 {
     boost::shared_ptr<implementation> sp1(new implementation());
     std::cout<<"The Samplenow has "<<sp1.use_count()<<" references\n";
 
     boost::shared_ptr<implementation> sp2 = sp1;
     std::cout<<"The Samplenow has "<<sp2.use_count()<<" references\n";
     
     sp1.reset();
     std::cout<<"AfterReset sp1. The Sample now has "<<sp2.use_count()<<"references\n";
 
     sp2.reset();
     std::cout<<"AfterReset sp2.\n";
 }
 
 void main()
 {
     test();
 }
该程序的输出结果如下:
The Sample nowhas 1 references
 The Sample now has 2 references
 After Reset sp1. The Sample now has 1 references
 destroying implementation
 After Reset sp2.
可以看到,boost::shared_ptr指针sp1和sp2同时拥有了implementation对象的访问权限,且当sp1和sp2都释放对该对象的所有权时,其所管理的的对象的内存才被自动释放。在共享对象的访问权限同时,也实现了其内存的自动管理。
boost::shared_ptr的内存管理机制:
boost::shared_ptr的管理机制其实并不复杂,就是对所管理的对象进行了引用计数,当新增一个boost::shared_ptr对该对象进行管理时,就将该对象的引用计数加一;减少一个boost::shared_ptr对该对象进行管理时,就将该对象的引用计数减一,如果该对象的引用计数为0的时候,说明没有任何指针对其管理,才调用delete释放其所占的内存。
上面的那个例子可以的图示如下:
1. sp1对implementation对象进行管理,其引用计数为1
2. 增加sp2对implementation对象进行管理,其引用计数增加为2
3. sp1释放对implementation对象进行管理,其引用计数变为1
4. sp2释放对implementation对象进行管理,其引用计数变为0,该对象被自动删除
boost::shared_ptr的特点:
和前面介绍的boost::scoped_ptr相比,boost::shared_ptr可以共享对象的所有权,因此其使用范围基本上没有什么限制(还是有一些需要遵循的使用规则,下文中介绍),自然也可以使用在stl的容器中。另外它还是线程安全的,这点在多线程程序中也非常重要。
boost::shared_ptr的使用规则:
boost::shared_ptr并不是绝对安全,下面几条规则能使我们更加安全的使用boost::shared_ptr:
1. 避免对shared_ptr所管理的对象的直接内存管理操作,以免造成该对象的重释放
2. shared_ptr并不能对循环引用的对象内存自动管理(这点是其它各种引用计数管理内存方式的通病)。
参考文章:http://www.cnblogs.com/TianFang/archive/2008/09/19/1294521.html
