C++ boost库中的sp_counted_base实现解析

来源:互联网 发布:php构造函数和析构函数 编辑:程序博客网 时间:2024/06/06 00:41

boost库中对sp_counted_base实现根据不同的体系配备了不同的实现体,这里主要分析x86体系下的gcc实现版本。

在正式介绍该函数之前,需要介绍三个函数的实现。

inline int atomic_exchange_and_add( int * pw, int dv ),这个函数的作用:

1. 返回pw存放的数值

2. 修改*pw = *pw + dv

只不过这两个操作都是原子操作,通过在锁操作数地址的情况下,xadd指令完成交换和加法操作。

inline void atomic_increment( int * pw ),该函数作用:

原子对pw里面存放的数值递增,也是在锁操作数地址的情况下,通过incl指令完成

inline int atomic_conditional_increment( int * pw ),该函数作用:

if(*pw) ++*pw

并返回修改前pw存放的数值,这里采用的是cmpxchgl指令,具体代码如下:

    int rv, tmp;    __asm__    (        "movl %0, %%eax\n\t"        "0:\n\t"        "test %%eax, %%eax\n\t"        "je 1f\n\t"        "movl %%eax, %2\n\t"        "incl %2\n\t"        "lock\n\t"        "cmpxchgl %2, %0\n\t"        "jne 0b\n\t"        "1:":        "=m"( *pw ), "=&a"( rv ), "=&r"( tmp ): // outputs (%0, %1, %2)        "m"( *pw ): // input (%3)        "cc" // clobbers    );
1. 将*pw存放的数值放入到eax寄存器中

2. 开始判断,如果eax寄存器中保存的数值=0,那么跳到7

3. 将eax里面的数值存放一份到tmp中

4. 对tmp里面的数值进行+1操作

5. 如果*pw里面的数值等于eax里面的数值(如果我们在将*pw的数值取出来之后,后续操作并没有改变*pw),那么将tmp数值写入到pw位置,并设置ZF=1,否则,将*pw里面的数值放入到eax中,也就是重新更新了eax里面的数值,让eax永远等于*pw里面存放的数值

6. 如果在我们准备修改*pw之前,*pw数值已经发生改变,说明这时候我们不能对*pw进行修改,否则会出现写后写的错误情况,此时就应该重新获取新的*pw,并在新的*pw上进行操作,并尝试在此对*pw进行修改,即跳转到2处。如果上步骤修改成功,那么就返回修改前的*pw数值

7. 返回*pw修改前的数值


sp_counted_base是包含在boost::detail命名空间下的一个类,大致原型如下:

<span style="font-size:18px;">class sp_counted_base{private:    sp_counted_base( sp_counted_base const & );    sp_counted_base & operator= ( sp_counted_base const & );    int use_count_;        // #shared    int weak_count_;       // #weak + (#shared != 0)public:    sp_counted_base(): use_count_( 1 ), weak_count_( 1 )    {    }    virtual ~sp_counted_base() // nothrow    {    }       virtual void dispose() = 0; // nothrow    virtual void destroy() // nothrow    {        delete this;    }    virtual void * get_deleter( sp_typeinfo const & ti ) = 0;    virtual void * get_untyped_deleter() = 0;    void add_ref_copy()    {        atomic_increment( &use_count_ );    }    bool add_ref_lock() // true on success    {        return atomic_conditional_increment( &use_count_ ) != 0;    }    void release() // nothrow    {        if( atomic_exchange_and_add( &use_count_, -1 ) == 1 )        {            dispose();            weak_release();        }    }    void weak_add_ref() // nothrow    {        atomic_increment( &weak_count_ );    }    void weak_release() // nothrow    {        if( atomic_exchange_and_add( &weak_count_, -1 ) == 1 )        {            destroy();        }    }    long use_count() const // nothrow    {        return static_cast<int const volatile &>( use_count_ );    }};</span>

首先从这个类的构造函数入口,其复制构造函数和赋值构造函数被声明为private,这样就避免了通过复制和赋值操作来产生一个新的对象

那么新的对象如何产生呢?

只能通过定义来产生一个默认的对象,并且这个对象里面的两个成员变量都被赋值为默认值,也就是不能再构造这个对象的时候手动的指定成员变量的值。说到成员变量,sp_counted_base有两个私有的成员变量。

use_count_,主要用于shared_ptr智能指针用来管理计数的。在后续的shared_ptr章节会有介绍。

weak_count_,用于weak_ptr智能指针用来管理计数的,在后续的weak_ptr会介绍。

说完构造函数,析构函数就比较简单了,由于这个类没有管理动态资源,故析构函数是一个空函数,并且实现为虚函数,主要用于管理子类的时候能够正确的调用子类的析构函数,而非错误的调用父类的析构函数。

  • destroy函数,这个函数就是去销毁一个对象,当然这个对象可能是自身或者子类,也定义为虚函数,方便后续不同的子类来复写,从而实现多态。
  • use_count函数,这个函数申明为const类型,表明这个函数不会对对象中的变量进行修改,返回use_count_变量的数值,只不过再返回的时候进行了类型的转换,这里使用了const volatile两个来修饰,说明这个变量首先对于返回值来说是一个常量,同时volatile同时告诉编译器这个常量的读取不能够从缓存在寄存器中进行读取,而要从内存中去读取,以防止数据的不一致性。
  • add_ref_copy函数,调用atomic_increament函数对use_count_变量进行原子+1操作,这个函数主要供shared_ptr调用
  • add_ref_lock函数,调用atomic_conditional_increament函数对use_count_变量+1操作,主要用于weak_ptr到shared_ptr转换时候使用,因为一个weak_ptr对象,如果计数为0,那么就无法转换为一个shared_ptr对象,这里正是通过atomic_conditional_increament函数来正确的管理计数。
  • weak_add_ref函数,类型于add_ref_copy,只不过区别是对weak_count_变量+1操作
  • weak_release函数,进行两部操作,首先将weak_count_修改为weak_count_-1,然后判断weak_count以前的数值是否为1,如果为1说明目前这个调用weak_release函数的对象是最后一个拥有某项资源的对象,那么就调用destory来释放这个对象,当然根据destory函数是一个虚函数,会释放具体的子类。
  • release函数,这个函数是对use_count_变量进行类型与weak_release的操作,一点区别是不会直接调用destory函数,取而代之的是调用dispose和weak_release函数,其中dispose函数是一个纯虚函数,需要子类具体实现,目的是用于释放该对象所管理的具体资源,也就是给资源销毁一个时机。并通过weak_release来销毁该对象。
  • 还有两个纯虚函数get_deleter和get_untyped_deleter,都需要子类去具体实现。

0 0
原创粉丝点击