Boost库智能指针简析

(一)Boost库中,智能指针并不只shared_ptr一个。同族但有不同的功能目标的还有如下5个:
scoped_ptr
scoped_array
shared_ptr
shared_array
weak_ptr

          scoped_ptrscoped_array与标准C++中的智能指针auto_ptr功能基本类似,不过它不传递所有权,不可复制。它不能共享所有权的特性却大大限制了其使用范围,而boost::shared_ptr可以解决这一局限。顾名思义,boost::shared_ptr是可以共享所有权的智能指针,其主要目标就是在小范围,小作用域中使用,以减少显式的delete, new配对操作,提高代码的安全性。scoped_ptr是针对指针的版本,scoped_array同是专门针对数组的。shared_arrayshared_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指针sp1sp2同时拥有了implementation对象的访问权限,且当sp1sp2都释放对该对象的所有权时,其所管理的的对象的内存才被自动释放。在共享对象的访问权限同时,也实现了其内存的自动管理。

boost::shared_ptr的内存管理机制:

boost::shared_ptr的管理机制其实并不复杂,就是对所管理的对象进行了引用计数,当新增一个boost::shared_ptr对该对象进行管理时,就将该对象的引用计数加一;减少一个boost::shared_ptr对该对象进行管理时,就将该对象的引用计数减一,如果该对象的引用计数为0的时候,说明没有任何指针对其管理,才调用delete释放其所占的内存。

上面的那个例子可以的图示如下:

1.  sp1implementation对象进行管理,其引用计数为

2.  增加sp2implementation对象进行管理,其引用计数增加为

3.  sp1释放对implementation对象进行管理,其引用计数变为

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

你可能感兴趣的:(C++)