zoukankan      html  css  js  c++  java
  • C++11智能指针

    今晚跟同学谈了一下智能指针,突然想要看一下C++11的智能指针的实现,因此下了这篇博文。

    以下代码出自于VS2012 <memory>

      1 template<class _Ty>
      2     class shared_ptr
      3         : public _Ptr_base<_Ty>
      4     {    // class for reference counted resource management
      5 public:
      6     typedef shared_ptr<_Ty> _Myt;
      7     typedef _Ptr_base<_Ty> _Mybase;
      8 
      9     shared_ptr() _NOEXCEPT
     10         {    // construct empty shared_ptr object
     11         }
     12 
     13     template<class _Ux>
     14         explicit shared_ptr(_Ux *_Px)
     15         {    // construct shared_ptr object that owns _Px
     16         _Resetp(_Px);
     17         }
     18 
     19     template<class _Ux,
     20         class _Dx>
     21         shared_ptr(_Ux *_Px, _Dx _Dt)
     22         {    // construct with _Px, deleter
     23         _Resetp(_Px, _Dt);
     24         }
     25 
     26 //#if _HAS_CPP0X
     27 
     28     shared_ptr(nullptr_t)
     29         {    // construct with nullptr
     30         _Resetp((_Ty *)0);
     31         }
     32 
     33     template<class _Dx>
     34         shared_ptr(nullptr_t, _Dx _Dt)
     35         {    // construct with nullptr, deleter
     36         _Resetp((_Ty *)0, _Dt);
     37         }
     38 
     39     template<class _Dx,
     40         class _Alloc>
     41         shared_ptr(nullptr_t, _Dx _Dt, _Alloc _Ax)
     42         {    // construct with nullptr, deleter, allocator
     43         _Resetp((_Ty *)0, _Dt, _Ax);
     44         }
     45 
     46     template<class _Ux,
     47         class _Dx,
     48         class _Alloc>
     49         shared_ptr(_Ux *_Px, _Dx _Dt, _Alloc _Ax)
     50         {    // construct with _Px, deleter, allocator
     51         _Resetp(_Px, _Dt, _Ax);
     52         }
     53 //#endif /* _HAS_CPP0X */
     54 
     55  #if _HAS_CPP0X
     56     template<class _Ty2>
     57         shared_ptr(const shared_ptr<_Ty2>& _Right, _Ty *_Px) _NOEXCEPT
     58         {    // construct shared_ptr object that aliases _Right
     59         this->_Reset(_Px, _Right);
     60         }
     61  #endif /* _HAS_CPP0X */
     62 
     63     shared_ptr(const _Myt& _Other) _NOEXCEPT
     64         {    // construct shared_ptr object that owns same resource as _Other
     65         this->_Reset(_Other);
     66         }
     67 
     68     template<class _Ty2>
     69         shared_ptr(const shared_ptr<_Ty2>& _Other,
     70             typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
     71                 void>::type ** = 0) _NOEXCEPT
     72         {    // construct shared_ptr object that owns same resource as _Other
     73         this->_Reset(_Other);
     74         }
     75 
     76     template<class _Ty2>
     77         explicit shared_ptr(const weak_ptr<_Ty2>& _Other,
     78             bool _Throw = true)
     79         {    // construct shared_ptr object that owns resource *_Other
     80         this->_Reset(_Other, _Throw);
     81         }
     82 
     83     template<class _Ty2>
     84         shared_ptr(auto_ptr<_Ty2>&& _Other)
     85         {    // construct shared_ptr object that owns *_Other.get()
     86         this->_Reset(_STD move(_Other));
     87         }
     88 
     89     template<class _Ty2>
     90         shared_ptr(const shared_ptr<_Ty2>& _Other, const _Static_tag& _Tag)
     91         {    // construct shared_ptr object for static_pointer_cast
     92         this->_Reset(_Other, _Tag);
     93         }
     94 
     95     template<class _Ty2>
     96         shared_ptr(const shared_ptr<_Ty2>& _Other, const _Const_tag& _Tag)
     97         {    // construct shared_ptr object for const_pointer_cast
     98         this->_Reset(_Other, _Tag);
     99         }
    100 
    101     template<class _Ty2>
    102         shared_ptr(const shared_ptr<_Ty2>& _Other, const _Dynamic_tag& _Tag)
    103         {    // construct shared_ptr object for dynamic_pointer_cast
    104         this->_Reset(_Other, _Tag);
    105         }
    106 
    107     shared_ptr(_Myt&& _Right) _NOEXCEPT
    108         : _Mybase(_STD forward<_Myt>(_Right))
    109         {    // construct shared_ptr object that takes resource from _Right
    110         }
    111 
    112     template<class _Ty2>
    113         shared_ptr(shared_ptr<_Ty2>&& _Right,
    114             typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
    115                 void>::type ** = 0) _NOEXCEPT
    116         : _Mybase(_STD forward<shared_ptr<_Ty2> >(_Right))
    117         {    // construct shared_ptr object that takes resource from _Right
    118         }
    119 
    120  #if _HAS_CPP0X
    121     template<class _Ux,
    122         class _Dx>
    123         shared_ptr(unique_ptr<_Ux, _Dx>&& _Right)
    124         {    // construct from unique_ptr
    125         _Resetp(_Right.release(), _Right.get_deleter());
    126         }
    127 
    128     template<class _Ux,
    129         class _Dx>
    130         _Myt& operator=(unique_ptr<_Ux, _Dx>&& _Right)
    131         {    // move from unique_ptr
    132         shared_ptr(_STD move(_Right)).swap(*this);
    133         return (*this);
    134         }
    135  #endif /* _HAS_CPP0X */
    136 
    137     _Myt& operator=(_Myt&& _Right) _NOEXCEPT
    138         {    // construct shared_ptr object that takes resource from _Right
    139         shared_ptr(_STD move(_Right)).swap(*this);
    140         return (*this);
    141         }
    142 
    143     template<class _Ty2>
    144         _Myt& operator=(shared_ptr<_Ty2>&& _Right) _NOEXCEPT
    145         {    // construct shared_ptr object that takes resource from _Right
    146         shared_ptr(_STD move(_Right)).swap(*this);
    147         return (*this);
    148         }
    149 
    150     ~shared_ptr() _NOEXCEPT
    151         {    // release resource
    152         this->_Decref();
    153         }
    154 
    155     _Myt& operator=(const _Myt& _Right) _NOEXCEPT
    156         {    // assign shared ownership of resource owned by _Right
    157         shared_ptr(_Right).swap(*this);
    158         return (*this);
    159         }
    160 
    161     template<class _Ty2>
    162         _Myt& operator=(const shared_ptr<_Ty2>& _Right) _NOEXCEPT
    163         {    // assign shared ownership of resource owned by _Right
    164         shared_ptr(_Right).swap(*this);
    165         return (*this);
    166         }
    167 
    168     template<class _Ty2>
    169         _Myt& operator=(auto_ptr<_Ty2>&& _Right)
    170         {    // assign ownership of resource pointed to by _Right
    171         shared_ptr(_STD move(_Right)).swap(*this);
    172         return (*this);
    173         }
    174 
    175     void reset() _NOEXCEPT
    176         {    // release resource and convert to empty shared_ptr object
    177         shared_ptr().swap(*this);
    178         }
    179 
    180     template<class _Ux>
    181         void reset(_Ux *_Px)
    182         {    // release, take ownership of _Px
    183         shared_ptr(_Px).swap(*this);
    184         }
    185 
    186     template<class _Ux,
    187         class _Dx>
    188         void reset(_Ux *_Px, _Dx _Dt)
    189         {    // release, take ownership of _Px, with deleter _Dt
    190         shared_ptr(_Px, _Dt).swap(*this);
    191         }
    192 
    193 //#if _HAS_CPP0X
    194     template<class _Ux,
    195         class _Dx,
    196         class _Alloc>
    197         void reset(_Ux *_Px, _Dx _Dt, _Alloc _Ax)
    198         {    // release, take ownership of _Px, with deleter _Dt, allocator _Ax
    199         shared_ptr(_Px, _Dt, _Ax).swap(*this);
    200         }
    201 //#endif /* _HAS_CPP0X */
    202 
    203     void swap(_Myt& _Other) _NOEXCEPT
    204         {    // swap pointers
    205         this->_Swap(_Other);
    206         }
    207 
    208     _Ty *get() const _NOEXCEPT
    209         {    // return pointer to resource
    210         return (this->_Get());
    211         }
    212 
    213     typename add_reference<_Ty>::type operator*() const _NOEXCEPT
    214         {    // return reference to resource
    215         return (*this->_Get());
    216         }
    217 
    218     _Ty *operator->() const _NOEXCEPT
    219         {    // return pointer to resource
    220         return (this->_Get());
    221         }
    222 
    223     bool unique() const _NOEXCEPT
    224         {    // return true if no other shared_ptr object owns this resource
    225         return (this->use_count() == 1);
    226         }
    227 
    228     _TYPEDEF_BOOL_TYPE;
    229 
    230     _OPERATOR_BOOL() const _NOEXCEPT
    231         {    // test if shared_ptr object owns no resource
    232         return (this->_Get() != 0 ? _CONVERTIBLE_TO_TRUE : 0);
    233         }
    234 
    235 private:
    236     template<class _Ux>
    237         void _Resetp(_Ux *_Px)
    238         {    // release, take ownership of _Px
    239         _TRY_BEGIN    // allocate control block and reset
    240         _Resetp0(_Px, new _Ref_count<_Ux>(_Px));
    241         _CATCH_ALL    // allocation failed, delete resource
    242         delete _Px;
    243         _RERAISE;
    244         _CATCH_END
    245         }
    246 
    247     template<class _Ux,
    248         class _Dx>
    249         void _Resetp(_Ux *_Px, _Dx _Dt)
    250         {    // release, take ownership of _Px, deleter _Dt
    251         _TRY_BEGIN    // allocate control block and reset
    252         _Resetp0(_Px, new _Ref_count_del<_Ux, _Dx>(_Px, _Dt));
    253         _CATCH_ALL    // allocation failed, delete resource
    254         _Dt(_Px);
    255         _RERAISE;
    256         _CATCH_END
    257         }
    258 
    259 //#if _HAS_CPP0X
    260     template<class _Ux,
    261         class _Dx,
    262         class _Alloc>
    263         void _Resetp(_Ux *_Px, _Dx _Dt, _Alloc _Ax)
    264         {    // release, take ownership of _Px, deleter _Dt, allocator _Ax
    265         typedef _Ref_count_del_alloc<_Ux, _Dx, _Alloc> _Refd;
    266         typename _Alloc::template rebind<_Refd>::other _Al = _Ax;
    267 
    268         _TRY_BEGIN    // allocate control block and reset
    269         _Refd *_Ptr = _Al.allocate(1);
    270         ::new (_Ptr) _Refd(_Px, _Dt, _Al);
    271         _Resetp0(_Px, _Ptr);
    272         _CATCH_ALL    // allocation failed, delete resource
    273         _Dt(_Px);
    274         _RERAISE;
    275         _CATCH_END
    276         }
    277 //#endif /* _HAS_CPP0X */
    278 
    279 public:
    280     template<class _Ux>
    281         void _Resetp0(_Ux *_Px, _Ref_count_base *_Rx)
    282         {    // release resource and take ownership of _Px
    283         this->_Reset0(_Px, _Rx);
    284         _Enable_shared(_Px, _Rx);
    285         }
    286     };

    我们可以看到shared_ptr是继承于_Ptr_base的,(同时weak_ptr也继承与_Ptr_base)

    那么我们先来看一下_Ptr_base里有什么东西

    首先我们可以看到_Ptr_base里面有两个属性

    private:
        _Ty *_Ptr;
        _Ref_count_base *_Rep;

    从shared_ptr我们知道_Ptr_base是个模板,而_Ty是传到_Ptr_base里的模板参数,也就是指针的类型

    所以我们知道 _Ptr 保存的值就是真正的指针

    但是 _Ref_count_base *_Rep 是什么东西呢,很明显就是引用计数。为什么要用指针呢,因为拥有相同_Ptr值的智能指针要拥有同一个引用计数,因此 _Rep 必须为指针。我们把引用计数类_Ref_count_base 放到后面去讨论。

    我们继续看一下shared_ptr的源码可以发现shared_ptr没有显式调用_Ptr_base的构造函数,这意味着shared_ptr只调用_Ptr_base的默认构造函数,但是

    shared_ptr的构造函数里大量的调用了两个函数 _Resetp 和 _Reset。

    ------------------------------------------------------------ _Ptr_base的构造函数 -------------------------------------------------------------------

    我们先看一下_Ptr_base的构造函数

    _Ptr_base()
            : _Ptr(0), _Rep(0)
            {    // construct
            }
    
        _Ptr_base(_Myt&& _Right)
            : _Ptr(0), _Rep(0)
            {    // construct _Ptr_base object that takes resource from _Right
            _Assign_rv(_STD forward<_Myt>(_Right));
            }
    
        template<class _Ty2>
            _Ptr_base(_Ptr_base<_Ty2>&& _Right)
            : _Ptr(_Right._Ptr), _Rep(_Right._Rep)
            {    // construct _Ptr_base object that takes resource from _Right
            _Right._Ptr = 0;
            _Right._Rep = 0;
            }

    _Ptr_base的默认构造函数是指针置位nullptr,这没什么好说的。剩下两个是转移构造函数,比较奇怪的是

    template<class _Ty2>
            _Ptr_base(_Ptr_base<_Ty2>&& _Right)

    接受以任意类型作为模板参数的_Ptr_base?不懂,估计与shared_ptr向上转型有关。

     ------------------------------------------------------------ _Ptr_base的_Resetp函数 -------------------------------------------------------------

    然后我们看一下_Resetp函数

    template<class _Ux>
            void _Resetp(_Ux *_Px)
            {    // release, take ownership of _Px
            _TRY_BEGIN    // allocate control block and reset
            _Resetp0(_Px, new _Ref_count<_Ux>(_Px));
            _CATCH_ALL    // allocation failed, delete resource
            delete _Px;
            _RERAISE;
            _CATCH_END
            }
    
        template<class _Ux,
            class _Dx>
            void _Resetp(_Ux *_Px, _Dx _Dt)
            {    // release, take ownership of _Px, deleter _Dt
            _TRY_BEGIN    // allocate control block and reset
            _Resetp0(_Px, new _Ref_count_del<_Ux, _Dx>(_Px, _Dt));
            _CATCH_ALL    // allocation failed, delete resource
            _Dt(_Px);
            _RERAISE;
            _CATCH_END
            }
    
    //#if _HAS_CPP0X
        template<class _Ux,
            class _Dx,
            class _Alloc>
            void _Resetp(_Ux *_Px, _Dx _Dt, _Alloc _Ax)
            {    // release, take ownership of _Px, deleter _Dt, allocator _Ax
            typedef _Ref_count_del_alloc<_Ux, _Dx, _Alloc> _Refd;
            typename _Alloc::template rebind<_Refd>::other _Al = _Ax;
    
            _TRY_BEGIN    // allocate control block and reset
            _Refd *_Ptr = _Al.allocate(1);
            ::new (_Ptr) _Refd(_Px, _Dt, _Al);
            _Resetp0(_Px, _Ptr);
            _CATCH_ALL    // allocation failed, delete resource
            _Dt(_Px);
            _RERAISE;
            _CATCH_END
            }

    _Resetp函数有三个重载,实际上就是 是否带析构器_Dx 和 是否带构造器_Alloc, 这两个参数都用于引用计数,我们继续留到后面讨论。

    _Resetp函数的三个重载里又都调用了_Resetp0

    template<class _Ux>
            void _Resetp0(_Ux *_Px, _Ref_count_base *_Rx)
            {    // release resource and take ownership of _Px
            this->_Reset0(_Px, _Rx);
            _Enable_shared(_Px, _Rx);
            }

    这里又调用了父类_Ptr_base的_Reset0 和另一个函数_Enable_shared

    先看一下_Reset0

    void _Reset0(_Ty *_Other_ptr, _Ref_count_base *_Other_rep)
            {    // release resource and take new resource
            if (_Rep != 0)
                _Rep->_Decref();
            _Rep = _Other_rep;
            _Ptr = _Other_ptr;
            }

    就是检查一下当前_Ptr_base引用计数指针是否为空,非空就释放一个引用计数,然后更新指针值和引用计数值

    (为什么叫_Reset0? 0表示最基本的Reset?)

    再看一下_Enable_shared

    template<class _Ty>
        inline void _Enable_shared(_Ty *_Ptr, _Ref_count_base *_Refptr,
            typename _Ty::_EStype * = 0)
        {    // reset internal weak pointer
        if (_Ptr)
            _Do_enable(_Ptr,
                (enable_shared_from_this<typename _Ty::_EStype>*)_Ptr, _Refptr);
        }
    
    inline void _Enable_shared(const volatile void *, const volatile void *)
        {    // not derived from enable_shared_from_this; do nothing
        }

    这里用了模板的最特化匹配

    当_Ty有定义_EStype这个类型名的时候(也就是_Ty继承于enable_shared_from_this<_Ty>的时候)会调用第一个函数。

    这里我简单描述一下_Do_enable的作用:

    因为_Ty继承于enable_shared_from_this<typename _Ty::_EStype>(实际上_Ty::_EStype就是_Ty,有兴趣的朋友可以去看一下 enable_shared_from_this的源码),enable_shared_from_this<typename _Ty::_EStype>内部保存着一个weak_ptr<_Ty>的弱指针,而_Do_enable的作用就是更新一下这个弱指针的值(使用过shared_ptr的朋友都应该知道enable_shared_from_this是用于共享this指针,而这个共享this指针的操作就是通过这个weak_ptr达到的)。

     

     ------------------------------------------------------------ shared_ptr的构造函数 -------------------------------------------------------------

     

    接着我们看一下shared_ptr的调用了_reset的构造函数

     1 shared_ptr(const _Myt& _Other) _NOEXCEPT
     2         {    // construct shared_ptr object that owns same resource as _Other
     3         this->_Reset(_Other);
     4         }
     5 
     6     template<class _Ty2>
     7         shared_ptr(const shared_ptr<_Ty2>& _Other,
     8             typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
     9                 void>::type ** = 0) _NOEXCEPT
    10         {    // construct shared_ptr object that owns same resource as _Other
    11         this->_Reset(_Other);
    12         }
    13 
    14     template<class _Ty2>
    15         explicit shared_ptr(const weak_ptr<_Ty2>& _Other,
    16             bool _Throw = true)
    17         {    // construct shared_ptr object that owns resource *_Other
    18         this->_Reset(_Other, _Throw);
    19         }
    20 
    21     template<class _Ty2>
    22         shared_ptr(auto_ptr<_Ty2>&& _Other)
    23         {    // construct shared_ptr object that owns *_Other.get()
    24         this->_Reset(_STD move(_Other));
    25         }
    26 
    27     template<class _Ty2>
    28         shared_ptr(const shared_ptr<_Ty2>& _Other, const _Static_tag& _Tag)
    29         {    // construct shared_ptr object for static_pointer_cast
    30         this->_Reset(_Other, _Tag);
    31         }
    32 
    33     template<class _Ty2>
    34         shared_ptr(const shared_ptr<_Ty2>& _Other, const _Const_tag& _Tag)
    35         {    // construct shared_ptr object for const_pointer_cast
    36         this->_Reset(_Other, _Tag);
    37         }
    38 
    39     template<class _Ty2>
    40         shared_ptr(const shared_ptr<_Ty2>& _Other, const _Dynamic_tag& _Tag)
    41         {    // construct shared_ptr object for dynamic_pointer_cast
    42         this->_Reset(_Other, _Tag);
    43         }

    这里又用到了模板的最特化匹配,注意L1 - L12

      is_convertible<_Ty2 *, _Ty *>::value

    是C++11提供的一个类型测试模板,用于测试_Ty2 * 与 _Ty *之间是否有合法转换。当有的时候将调用函数L6 - L12,否则调用L1 - L4

    这也是shared_ptr<_Ty>可以向shared_ptr<_Ty的父类>向上转型的秘密。

    PS:注意shared_ptr还有一个转移构造函数与上面提到的_Ptr_base的转移构造函数相对应

    1 template<class _Ty2>
    2         shared_ptr(shared_ptr<_Ty2>&& _Right,
    3             typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
    4                 void>::type ** = 0) _NOEXCEPT
    5         : _Mybase(_STD forward<shared_ptr<_Ty2> >(_Right))
    6         {    // construct shared_ptr object that takes resource from _Right
    7         }
    View Code

    然后L27 - L43的三个构造函数分别用于static_cast、const_cast、dynamic_cast转型。

    转型函数见

    template<class _Ty1,
        class _Ty2>
        shared_ptr<_Ty1>
            static_pointer_cast(const shared_ptr<_Ty2>& _Other) _NOEXCEPT
        {    // return shared_ptr object holding static_cast<_Ty1 *>(_Other.get())
        return (shared_ptr<_Ty1>(_Other, _Static_tag()));
        }
    
    template<class _Ty1,
        class _Ty2>
        shared_ptr<_Ty1>
            const_pointer_cast(const shared_ptr<_Ty2>& _Other) _NOEXCEPT
        {    // return shared_ptr object holding const_cast<_Ty1 *>(_Other.get())
        return (shared_ptr<_Ty1>(_Other, _Const_tag()));
        }
    
    template<class _Ty1,
        class _Ty2>
        shared_ptr<_Ty1>
            dynamic_pointer_cast(const shared_ptr<_Ty2>& _Other) _NOEXCEPT
        {    // return shared_ptr object holding dynamic_cast<_Ty1 *>(_Other.get())
        return (shared_ptr<_Ty1>(_Other, _Dynamic_tag()));
        }

    ------------------------------------------------------------ _Ptr_base的_Reset函数 -------------------------------------------------------------

     

    我们最后我们看一下_Ptr_base的_Reset函数

     1 void _Reset()
     2         {    // release resource
     3         _Reset(0, 0);
     4         }
     5 
     6     template<class _Ty2>
     7         void _Reset(const _Ptr_base<_Ty2>& _Other)
     8         {    // release resource and take ownership of _Other._Ptr
     9         _Reset(_Other._Ptr, _Other._Rep);
    10         }
    11 
    12     template<class _Ty2>
    13         void _Reset(const _Ptr_base<_Ty2>& _Other, bool _Throw)
    14         {    // release resource and take ownership from weak_ptr _Other._Ptr
    15         _Reset(_Other._Ptr, _Other._Rep, _Throw);
    16         }
    17 
    18     template<class _Ty2>
    19         void _Reset(const _Ptr_base<_Ty2>& _Other, const _Static_tag&)
    20         {    // release resource and take ownership of _Other._Ptr
    21         _Reset(static_cast<_Elem *>(_Other._Ptr), _Other._Rep);
    22         }
    23 
    24     template<class _Ty2>
    25         void _Reset(const _Ptr_base<_Ty2>& _Other, const _Const_tag&)
    26         {    // release resource and take ownership of _Other._Ptr
    27         _Reset(const_cast<_Elem *>(_Other._Ptr), _Other._Rep);
    28         }
    29 
    30     template<class _Ty2>
    31         void _Reset(const _Ptr_base<_Ty2>& _Other, const _Dynamic_tag&)
    32         {    // release resource and take ownership of _Other._Ptr
    33         _Elem *_Ptr = dynamic_cast<_Elem *>(_Other._Ptr);
    34         if (_Ptr)
    35             _Reset(_Ptr, _Other._Rep);
    36         else
    37             _Reset();
    38         }
    39 
    40     template<class _Ty2>
    41         void _Reset(auto_ptr<_Ty2>&& _Other)
    42         {    // release resource and take _Other.get()
    43         _Ty2 *_Px = _Other.get();
    44         _Reset0(_Px, new _Ref_count<_Elem>(_Px));
    45         _Other.release();
    46         _Enable_shared(_Px, _Rep);
    47         }
    48 
    49  #if _HAS_CPP0X
    50     template<class _Ty2>
    51         void _Reset(_Ty *_Ptr, const _Ptr_base<_Ty2>& _Other)
    52         {    // release resource and alias _Ptr with _Other_rep
    53         _Reset(_Ptr, _Other._Rep);
    54         }
    55  #endif /* _HAS_CPP0X */
    56 
    57     void _Reset(_Ty *_Other_ptr, _Ref_count_base *_Other_rep)
    58         {    // release resource and take _Other_ptr through _Other_rep
    59         if (_Other_rep)
    60             _Other_rep->_Incref();
    61         _Reset0(_Other_ptr, _Other_rep);
    62         }
    63 
    64     void _Reset(_Ty *_Other_ptr, _Ref_count_base *_Other_rep, bool _Throw)
    65         {    // take _Other_ptr through _Other_rep from weak_ptr if not expired
    66             // otherwise, leave in default state if !_Throw,
    67             // otherwise throw exception
    68         if (_Other_rep && _Other_rep->_Incref_nz())
    69             _Reset0(_Other_ptr, _Other_rep);
    70         else if (_Throw)
    71             _THROW_NCEE(bad_weak_ptr, 0);
    72         }
    View Code

    实际上也就是转发一下到_Reset0函数

    ------------------------------------------------------------ _Ptr_base的_Resetw函数 ------------------------------------------------------------

    除此之外,我们还可以发现_Ptr_base中还有几个类似的函数_Resetw,这几个函数是为了被weak_ptr调用的,在这里我们不详细说,但在下面讨论_Ref_count_base 的时候会被提及。

     1 void _Resetw()
     2         {    // release weak reference to resource
     3         _Resetw((_Elem *)0, 0);
     4         }
     5 
     6     template<class _Ty2>
     7         void _Resetw(const _Ptr_base<_Ty2>& _Other)
     8         {    // release weak reference to resource and take _Other._Ptr
     9         _Resetw(_Other._Ptr, _Other._Rep);
    10         }
    11 
    12     template<class _Ty2>
    13         void _Resetw(const _Ty2 *_Other_ptr, _Ref_count_base *_Other_rep)
    14         {    // point to _Other_ptr through _Other_rep
    15         _Resetw(const_cast<_Ty2*>(_Other_ptr), _Other_rep);
    16         }
    17 
    18     template<class _Ty2>
    19         void _Resetw(_Ty2 *_Other_ptr, _Ref_count_base *_Other_rep)
    20         {    // point to _Other_ptr through _Other_rep
    21         if (_Other_rep)
    22             _Other_rep->_Incwref();
    23         if (_Rep != 0)
    24             _Rep->_Decwref();
    25         _Rep = _Other_rep;
    26         _Ptr = _Other_ptr;
    27         }

    --------------------------------------------------------------- _Ref_count_base ---------------------------------------------------------------

    _Ref_count_base有两个虚函数,_Ref_count_base的几个子类(带析构器和带构造器)只是override了这两个函数,来产生不同的指针析构行为和自身析构行为。

    virtual void _Destroy() = 0;
    virtual void _Delete_this() = 0;

    因此我们只需要研究_Ref_count_base本身就好。

    _Ref_count_base带有两个数据成员(指针数据成员在具体子类里面)

    _Atomic_counter_t _Uses;
    _Atomic_counter_t _Weaks;

    从名字可以猜测出来 _Uses 是shared_ptr的引用计数, _Weaks 是weak_ptr的引用计数

    为什么我们需要 _Weaks 呢? 因为在_Uses 引用计数为0(最后一个shared_ptr已经被析构)的时候我们就应该析构掉真正的指针,但问题是这个引用计数对象本身也是一个指针,那么这个引用计数也要在这时候被析构吗?使用过shared_ptr的朋友会知道,shared_ptr 有一个与之紧密相连的类 weak_ptr 实际上由一个shared_ptr 产生的weak_ptr是共享同一个引用计数对象的(这样子weak_ptr就可以知道真正的指针是否被析构掉了)。如果所以 shared_ptr 都被析构掉了同时其引用计数对象,但析构掉了,但有这个 shared_ptr  产生的 weak_ptr 仍然存在那么就可能导致 weak_ptr 访问一个已经被析构的指针。 因此应该是所有的 shared_ptr 与其 产生的weak_ptr 都被析构掉了,其引用计数对象才被析构掉。

    我们可以从下面的减少引用计数函数看出来。

    PS: #define _MT_DECR  _InterlockedIncrement(reinterpret_cast<volatile long *>(&x))

     1   void _Decref()
     2         {    // decrement use count
     3         if (_MT_DECR(_Ignored, _Uses) == 0)
     4             {    // destroy managed resource, decrement weak reference count
     5             _Destroy();
     6             _Decwref();
     7             }
     8         }
     9 
    10     void _Decwref()
    11         {    // decrement weak reference count
    12         if (_MT_DECR(_Ignored, _Weaks) == 0)
    13             _Delete_this();
    14         }

    与上面相对应的增加引用计数函数

    PS: #define _MT_INCR(mtx, x)_InterlockedIncrement(reinterpret_cast<volatile long *>(&x))

    1   void _Incref()
    2         {    // increment use count
    3         _MT_INCR(_Ignored, _Uses);
    4         }
    5 
    6     void _Incwref()
    7         {    // increment weak reference count
    8         _MT_INCR(_Ignored, _Weaks);
    9         }

    再补上 _Ref_count_base 的构造函数

    1   _Ref_count_base()
    2         {    // construct
    3         _Init_atomic_counter(_Uses, 1);
    4         _Init_atomic_counter(_Weaks, 1);
    5         }

    我们可以看到 _Ref_count_base 构造函数中对 _Uses 与 _Weaks 初始化引用计数为1,_Uses 为0时析构指针, _Weaks 为0时析构引用计数对象。

    比较有趣的是我们可以从 weak_ptr 指针 lock 出一个 shared_ptr 指针的时候,会调用_Ref_count_base 类的函数 _Incref_nz,这个函数检查引用计数对象的引用计数是否为0,非零(未析构真正指针)的时候就可以增加一个引用计数。这里面为了 Lock-Free 调用了函数 _InterlockedCompareExchange。

     1 bool _Incref_nz()
     2         {    // increment use count if not zero, return true if successful
     3         for (; ; )
     4             {    // loop until state is known
     5  #if defined(_M_IX86) || defined(_M_X64) || defined(_M_CEE_PURE)
     6             _Atomic_integral_t _Count =
     7                 static_cast<volatile _Atomic_counter_t&>(_Uses);
     8 
     9             if (_Count == 0)
    10                 return (false);
    11 
    12             if (static_cast<_Atomic_integral_t>(_InterlockedCompareExchange(
    13                     reinterpret_cast<volatile long *>(&_Uses),
    14                     _Count + 1, _Count)) == _Count)
    15                 return (true);
    16  #else /* defined(_M_IX86) || defined(_M_X64) || defined(_M_CEE_PURE) */
    17             _Atomic_integral_t _Count =
    18                 _Load_atomic_counter_explicit(_Uses, memory_order_relaxed);
    19 
    20             if (_Count == 0)
    21                 return (false);
    22 
    23             if (_Compare_increment_atomic_counter_explicit(
    24                     _Uses, _Count, memory_order_relaxed))
    25                 return (true);
    26  #endif /* defined(_M_IX86) || defined(_M_X64) || defined(_M_CEE_PURE) */
    27             }
    28         }

     因为 _Ref_count_base 里面的引用计数增加/减少都是Lock-Free的,因此对shared_ptr的引用计数是多线程安全的。

     --------------------------------------------------------------- shared_ptr的线程安全---------------------------------------------------------------

     多个线程同时对同一个shared_ptr的写操作是不安全的,因为其swap函数

    1   void swap(_Myt& _Other) _NOEXCEPT
    2         {    // swap pointers
    3         this->_Swap(_Other);
    4         }

    调用了一个非线程安全函数_Ptr_base的_Swap

    1   void _Swap(_Ptr_base& _Right)
    2         {    // swap pointers
    3         _STD swap(_Rep, _Right._Rep);
    4         _STD swap(_Ptr, _Right._Ptr);
    5         }

    但是多个线程同时对共享引用计数的不同shared_ptr的写操作是安全的,因为对于真正的指针,shared_ptr只对其进行简单的读写不修改其指向的对象的内部状态,而且同一时刻只有一个线程对某个shared_ptr真正的指针进行读写,因此线程安全的。对于引用计数对象,虽然修改了其内部状态,但本身这种修改动作是线程安全的。所以我们可以推论多个线程同时对共享引用计数的不同shared_ptr的写操作也是安全的。

    boost库对shared_ptr的描述也证明了这一点。

    shared_ptr objects offer the same level of thread safety as builtin types.

     A shared_ptr instance can be "read" (accessed using only const operations) simultaneously by multiple threads. 

    Different shared_ptr instances can be "written to" (accessed using mutable operations such as operator= or reset) simultaneosly 

    by multiple threads (even when these instances are copies, and share the same reference count underneath.)

    Any other simultaneous accesses result in undefined behavior.

    翻译为中文如下:

    shared_ptr 对象提供与内建类型一样的线程安全级别。一个 shared_ptr 实例可以同时被多个线程“读”(仅使用不变操作进行访问)。 不同的 shared_ptr 实例可以同时被多个线程“写入”(使用类似 operator= 或 reset 这样的可变操作进行访问)(即使这些实 例是拷贝,而且共享下层的引用计数)。
    任何其它的同时访问的结果会导致未定义行为。”

  • 相关阅读:
    JetBrains 里不为人知的秘密(2)--快捷键篇
    phpstorm 2017之输入法
    JetBrains 里不为人知的秘密
    Windows Server 2012 远程连接
    visibility和display的区别
    【dp】摘花生
    【dp】 背包问题
    【dp】求最长公共子序列
    【dp】合唱队形
    【dp】友好城市
  • 原文地址:https://www.cnblogs.com/kirito/p/3637476.html
Copyright © 2011-2022 走看看