Boost学习--初窥shared_ptr

preface:
[无视废话,直奔主题]
When my dad was young, he liked to anatomize little devices into pieces, such as a mechanical clock. Maybe he wanted to understand how it works, or just marveled at the precise components . It was pity that he could not become a great engineer later, one reaseon was too few things for him to dissect i think.

Now,many excellent code libraries are opensource for us, we can ‘dismember’ them for study without worries about restoration. "boost" is one of them.

//---------------------------------------------------------------------------//

首先学习的是boost::shared_ptr。shared_ptr是智能指针的一种,可参考:

http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/smart_ptr.htm

打开VC,随便写了个简单小程序:

...

{

shared_ptr<Type> a(make_shared<Type>());

shared_ptr<Type> b =a;

}

...

(更详细的示例可见:http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/example/shared_ptr_example.cpp )


职业习惯,忍不住F11跟进去看看到底执行了什么,窥探一下智能指针的工作原理。

//---------------------------------------------------------------------------//

首先跟进shared_ptr<Type> a(createType());这一句:

这里调用了shared_ptr( Y * p ): px( p ), pn( p ):px是个Type类的指针,pn是个shared_count类,顾名思义,应该就是跟引用计数相关的。F11进shared_count的构造函数。shared_count只有一个成员sp_counted_base* pi_。这个pi_随后初始化指向了一个sp_counted_impl_p的new对象,sp_counted_impl_p是sp_counted_base的子类。F11进sp_counted_base的构造函数,可以看出这里有引用计数变量use_count_,还有用于weak_ptr进行的“监视”计数weak_count_;sp_counted_impl_p类就是多了个Type指针成员,其值和shared_ptr类里的px一样。到这里智能指针a算是构造成功了。

接着跟进shared_ptr<Type> b =a;这一句:

这里调用的是shared_ptr(share_ptr<T> const &),F11进去会发现,和上一句最大不同的地方在于shared_count(shared_count const & r): pi_(r.pi_)里面调用了sp_counted_base::add_ref_copy(),这个函数负责把sp_counted_base::use_count_加1;查看内存可知智能指针b的两个成员的值和a是一模一样的。

当这两个智能指针退出作用域时,将先析构b然后析构a。当然,就此处而言,我认为a和b的析构顺序交换了不会有影响。析构函数里调用了sp_counted_base::release(),当use_count_还大于0的时候减1。假如减完后use_count_和weak_count_为0,即再没有智能指针指向Type对象了,就如同这里第2个shared_ptr的析构函数的情况,此时可以把这个Type对象析构掉,还有引用计数对象也析构掉。

以上函数调用的过程可以简记如下:
1."shared_ptr<Type> a(make_shared<Type>());"
    shared_ptr( Y * p ): px( p )
        shared_count::shared_count( Y * p ): pi_( 0 )
            sp_counted_impl_p::sp_counted_impl_p( X * px ): px_( px )
                sp_counted_base::sp_counted_base()

2."shared_ptr<Type> b =a;"               
    shared_ptr(share_ptr<T> const &)
        shared_count(shared_count const & r): pi_(r.pi_)
            sp_counted_base::add_ref_copy()
                BOOST_INTERLOCKED_INCREMENT( &use_count_ )               

3.析构b
    shared_ptr<T>::~shared_ptr<T>
        shared_count::~shared_count
            sp_counted_base::release()
                BOOST_INTERLOCKED_DECREMENT( &use_count_ )
               
4.析构a               
    shared_ptr<T>::~shared_ptr<T>
        shared_count::~shared_count
            sp_counted_base::release()
                BOOST_INTERLOCKED_DECREMENT( &use_count_ )
                sp_counted_base::dispose()
                    boost::checked_delete( px_ )
                        delete px_ (T::~T)
                sp_counted_base::weak_release()
                    BOOST_INTERLOCKED_DECREMENT( &weak_count_ )
                    sp_counted_base::destroy()
                        delete this (sp_counted_impl_p<T>::~sp_counted_impl_p<T>)

//---------------------------------------------------------------------------//
           
从内存布局来分析shared_ptr,其实shared_ptr可以看作这样一个结构体:
shared_ptr
{
    T * px;
    shared_count pn;
}
而引用计数对象shared_count更简单,实际就是一个指针:
shared_count
{
    sp_counted_base * pi_;
}
这个指针指向了一个sp_counted_impl_p对象。sp_counted_base就是引用计数的实现:
sp_counted_base
{
    long use_count_;
    long weak_count_;
}
sp_counted_impl_p继承sp_counted_base,多了一个指向管理对象的指针:
sp_counted_impl_p
{
    sp_counted_base base;
    T * px;
}
这里列举出相关对象的大小:
sizeof(shared_ptr<T>) == 8
sizeof(shared_count<T>) == 4
sizeof(sp_counted_base) == 12 (加上虚函数表)
sizeof(sp_counted_impl_p<T>) == 16

shared_ptr内存布局的参考图:



以上测试均在VS2005中进行,boost的版本是1.46.1.
//---------------------------------------------------------------------------//

后记:
内存管理一直是C++的重大议题,如何权衡把握性能和安全是C++程序员的必修课。智能指针是一个很好的管理内存解决方案,以后在合适的场合下多用,充分发挥它的优势。
之前有个不很踏实的地方是线程安全,“听说”原来STL容器的实现因为线程不安全,实用性打了折扣。通过学习shared_ptr的源码,了解到引用计数临界区就是一个long变量,通过BOOST_INTERLOCKED_INCREMENT/DECREMENT(x86下是lock xadd指令)原子操作进行访问。在网上搜了下,没找到说shared_ptr不适合多核并行开发,可以放心使用。
此外,别看shared_ptr麻雀虽小,值得深究的地方还是不少。除了上面提到的那个lock-free的引用计数之外,例如shared_ptr本身的设计也挺有意思。shared_ptr很重要的一个用途是实现pimpl模式,使接口和实现分离。而shared_ptr里的shared_count成员本身也运用了这种模式,当然这里用的是原始指针,不知道如果递归定义会怎样,语义上能不能说得通?这太伤我脑筋了,还是不想为妙。此外shared_ptr的实现之所以如此精悍,皆因大牛们无数辛苦的汗水。听说最早shared_ptr的实现方案有好多种,什么引用计数、链表等等,经过对这些实现的充分对比测试,并权衡各方面因素,最终选定了现在这种实现。如果项目需要使用或者设计智能指针,了解这段历史过程是很有帮助的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值