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)ide

那麼咱們先來看一下_Ptr_base裏有什麼東西函數

 

首先咱們能夠看到_Ptr_base裏面有兩個屬性oop

private:
    _Ty *_Ptr;
    _Ref_count_base *_Rep;

從shared_ptr咱們知道_Ptr_base是個模板,而_Ty是傳到_Ptr_base裏的模板參數,也就是指針的類型測試

因此咱們知道 _Ptr 保存的值就是真正的指針ui

可是 _Ref_count_base *_Rep 是什麼東西呢,很明顯就是引用計數。爲何要用指針呢,由於擁有相同_Ptr值的智能指針要擁有同一個引用計數,所以 _Rep 必須爲指針。咱們把引用計數類_Ref_count_base 放到後面去討論。this

 

咱們繼續看一下shared_ptr的源碼能夠發現shared_ptr沒有顯式調用_Ptr_base的構造函數,這意味着shared_ptr只調用_Ptr_base的默認構造函數,可是atom

shared_ptr的構造函數裏大量的調用了兩個函數 _Resetp 和 _Reset。spa

 

------------------------------------------------------------ _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 這樣的可變操做進行訪問)(即便這些實 例是拷貝,並且共享下層的引用計數)。
任何其它的同時訪問的結果會致使未定義行爲。」

相關文章
相關標籤/搜索