打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
C++11智能指针

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

以下代码出自于VS2012 <memory>

001.template<class _Ty>
002.class shared_ptr
003.: public _Ptr_base<_Ty>
004.{    // class for reference counted resource management
005.public:
006.typedef shared_ptr<_Ty> _Myt;
007.typedef _Ptr_base<_Ty> _Mybase;
008. 
009.shared_ptr() _NOEXCEPT
010.{    // construct empty shared_ptr object
011.}
012. 
013.template<class _Ux>
014.explicit shared_ptr(_Ux *_Px)
015.{    // construct shared_ptr object that owns _Px
016._Resetp(_Px);
017.}
018. 
019.template<class _Ux,
020.class _Dx>
021.shared_ptr(_Ux *_Px, _Dx _Dt)
022.{    // construct with _Px, deleter
023._Resetp(_Px, _Dt);
024.}
025. 
026.//#if _HAS_CPP0X
027. 
028.shared_ptr(nullptr_t)
029.{    // construct with nullptr
030._Resetp((_Ty *)0);
031.}
032. 
033.template<class _Dx>
034.shared_ptr(nullptr_t, _Dx _Dt)
035.{    // construct with nullptr, deleter
036._Resetp((_Ty *)0, _Dt);
037.}
038. 
039.template<class _Dx,
040.class _Alloc>
041.shared_ptr(nullptr_t, _Dx _Dt, _Alloc _Ax)
042.{    // construct with nullptr, deleter, allocator
043._Resetp((_Ty *)0, _Dt, _Ax);
044.}
045. 
046.template<class _Ux,
047.class _Dx,
048.class _Alloc>
049.shared_ptr(_Ux *_Px, _Dx _Dt, _Alloc _Ax)
050.{    // construct with _Px, deleter, allocator
051._Resetp(_Px, _Dt, _Ax);
052.}
053.//#endif /* _HAS_CPP0X */
054. 
055.#if _HAS_CPP0X
056.template<class _Ty2>
057.shared_ptr(const shared_ptr<_Ty2>& _Right, _Ty *_Px) _NOEXCEPT
058.{    // construct shared_ptr object that aliases _Right
059.this->_Reset(_Px, _Right);
060.}
061.#endif /* _HAS_CPP0X */
062. 
063.shared_ptr(const _Myt& _Other) _NOEXCEPT
064.{    // construct shared_ptr object that owns same resource as _Other
065.this->_Reset(_Other);
066.}
067. 
068.template<class _Ty2>
069.shared_ptr(const shared_ptr<_Ty2>& _Other,
070.typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
071.void>::type ** = 0) _NOEXCEPT
072.{    // construct shared_ptr object that owns same resource as _Other
073.this->_Reset(_Other);
074.}
075. 
076.template<class _Ty2>
077.explicit shared_ptr(const weak_ptr<_Ty2>& _Other,
078.bool _Throw = true)
079.{    // construct shared_ptr object that owns resource *_Other
080.this->_Reset(_Other, _Throw);
081.}
082. 
083.template<class _Ty2>
084.shared_ptr(auto_ptr<_Ty2>&& _Other)
085.{    // construct shared_ptr object that owns *_Other.get()
086.this->_Reset(_STD move(_Other));
087.}
088. 
089.template<class _Ty2>
090.shared_ptr(const shared_ptr<_Ty2>& _Other, const _Static_tag& _Tag)
091.{    // construct shared_ptr object for static_pointer_cast
092.this->_Reset(_Other, _Tag);
093.}
094. 
095.template<class _Ty2>
096.shared_ptr(const shared_ptr<_Ty2>& _Other, const _Const_tag& _Tag)
097.{    // construct shared_ptr object for const_pointer_cast
098.this->_Reset(_Other, _Tag);
099.}
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里面有两个属性

1.private:
2._Ty *_Ptr;
3._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的构造函数

01._Ptr_base()
02.: _Ptr(0), _Rep(0)
03.{    // construct
04.}
05. 
06._Ptr_base(_Myt&& _Right)
07.: _Ptr(0), _Rep(0)
08.{    // construct _Ptr_base object that takes resource from _Right
09._Assign_rv(_STD forward<_Myt>(_Right));
10.}
11. 
12.template<class _Ty2>
13._Ptr_base(_Ptr_base<_Ty2>&& _Right)
14.: _Ptr(_Right._Ptr), _Rep(_Right._Rep)
15.{    // construct _Ptr_base object that takes resource from _Right
16._Right._Ptr = 0;
17._Right._Rep = 0;
18.}

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

1.template<class _Ty2>
2._Ptr_base(_Ptr_base<_Ty2>&& _Right)

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

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

然后我们看一下_Resetp函数

01.template<class _Ux>
02.void _Resetp(_Ux *_Px)
03.{    // release, take ownership of _Px
04._TRY_BEGIN    // allocate control block and reset
05._Resetp0(_Px, new _Ref_count<_Ux>(_Px));
06._CATCH_ALL    // allocation failed, delete resource
07.delete _Px;
08._RERAISE;
09._CATCH_END
10.}
11. 
12.template<class _Ux,
13.class _Dx>
14.void _Resetp(_Ux *_Px, _Dx _Dt)
15.{    // release, take ownership of _Px, deleter _Dt
16._TRY_BEGIN    // allocate control block and reset
17._Resetp0(_Px, new _Ref_count_del<_Ux, _Dx>(_Px, _Dt));
18._CATCH_ALL    // allocation failed, delete resource
19._Dt(_Px);
20._RERAISE;
21._CATCH_END
22.}
23. 
24.//#if _HAS_CPP0X
25.template<class _Ux,
26.class _Dx,
27.class _Alloc>
28.void _Resetp(_Ux *_Px, _Dx _Dt, _Alloc _Ax)
29.{    // release, take ownership of _Px, deleter _Dt, allocator _Ax
30.typedef _Ref_count_del_alloc<_Ux, _Dx, _Alloc> _Refd;
31.typename _Alloc::template rebind<_Refd>::other _Al = _Ax;
32. 
33._TRY_BEGIN    // allocate control block and reset
34._Refd *_Ptr = _Al.allocate(1);
35.::new (_Ptr) _Refd(_Px, _Dt, _Al);
36._Resetp0(_Px, _Ptr);
37._CATCH_ALL    // allocation failed, delete resource
38._Dt(_Px);
39._RERAISE;
40._CATCH_END
41.}

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

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

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

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

先看一下_Reset0

1.void _Reset0(_Ty *_Other_ptr, _Ref_count_base *_Other_rep)
2.{    // release resource and take new resource
3.if (_Rep != 0)
4._Rep->_Decref();
5._Rep = _Other_rep;
6._Ptr = _Other_ptr;
7.}

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

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

再看一下_Enable_shared

01.template<class _Ty>
02.inline void _Enable_shared(_Ty *_Ptr, _Ref_count_base *_Refptr,
03.typename _Ty::_EStype * = 0)
04.{    // reset internal weak pointer
05.if (_Ptr)
06._Do_enable(_Ptr,
07.(enable_shared_from_this<typename _Ty::_EStype>*)_Ptr, _Refptr);
08.}
09. 
10.inline void _Enable_shared(const volatile void *, const volatile void *)
11.{    // not derived from enable_shared_from_this; do nothing
12.}

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

当_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的构造函数

01.1 shared_ptr(const _Myt& _Other) _NOEXCEPT
02.2         {    // construct shared_ptr object that owns same resource as _Other
03.3         this->_Reset(_Other);
04.4         }
05.5
06.6     template<class _Ty2>
07.7         shared_ptr(const shared_ptr<_Ty2>& _Other,
08.8             typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
09.9                 void>::type ** = 0) _NOEXCEPT
10.10         {    // construct shared_ptr object that owns same resource as _Other
11.11         this->_Reset(_Other);
12.12         }
13.13
14.14     template<class _Ty2>
15.15         explicit shared_ptr(const weak_ptr<_Ty2>& _Other,
16.16             bool _Throw = true)
17.17         {    // construct shared_ptr object that owns resource *_Other
18.18         this->_Reset(_Other, _Throw);
19.19         }
20.20
21.21     template<class _Ty2>
22.22         shared_ptr(auto_ptr<_Ty2>&& _Other)
23.23         {    // construct shared_ptr object that owns *_Other.get()
24.24         this->_Reset(_STD move(_Other));
25.25         }
26.26
27.27     template<class _Ty2>
28.28         shared_ptr(const shared_ptr<_Ty2>& _Other, const _Static_tag& _Tag)
29.29         {    // construct shared_ptr object for static_pointer_cast
30.30         this->_Reset(_Other, _Tag);
31.31         }
32.32
33.33     template<class _Ty2>
34.34         shared_ptr(const shared_ptr<_Ty2>& _Other, const _Const_tag& _Tag)
35.35         {    // construct shared_ptr object for const_pointer_cast
36.36         this->_Reset(_Other, _Tag);
37.37         }
38.38
39.39     template<class _Ty2>
40.40         shared_ptr(const shared_ptr<_Ty2>& _Other, const _Dynamic_tag& _Tag)
41.41         {    // construct shared_ptr object for dynamic_pointer_cast
42.42         this->_Reset(_Other, _Tag);
43.43         }

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

1.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.1 template<class _Ty2>
2.2         shared_ptr(shared_ptr<_Ty2>&& _Right,
3.3             typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
4.4                 void>::type ** = 0) _NOEXCEPT
5.5         : _Mybase(_STD forward<shared_ptr<_Ty2> >(_Right))
6.6         {    // construct shared_ptr object that takes resource from _Right
7.7         }
View Code

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

转型函数见

01.template<class _Ty1,
02.class _Ty2>
03.shared_ptr<_Ty1>
04.static_pointer_cast(const shared_ptr<_Ty2>& _Other) _NOEXCEPT
05.{    // return shared_ptr object holding static_cast<_Ty1 *>(_Other.get())
06.return (shared_ptr<_Ty1>(_Other, _Static_tag()));
07.}
08. 
09.template<class _Ty1,
10.class _Ty2>
11.shared_ptr<_Ty1>
12.const_pointer_cast(const shared_ptr<_Ty2>& _Other) _NOEXCEPT
13.{    // return shared_ptr object holding const_cast<_Ty1 *>(_Other.get())
14.return (shared_ptr<_Ty1>(_Other, _Const_tag()));
15.}
16. 
17.template<class _Ty1,
18.class _Ty2>
19.shared_ptr<_Ty1>
20.dynamic_pointer_cast(const shared_ptr<_Ty2>& _Other) _NOEXCEPT
21.{    // return shared_ptr object holding dynamic_cast<_Ty1 *>(_Other.get())
22.return (shared_ptr<_Ty1>(_Other, _Dynamic_tag()));
23.}

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

 

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

01.1 void _Reset()
02.2         {    // release resource
03.3         _Reset(0, 0);
04.4         }
05.5
06.6     template<class _Ty2>
07.7         void _Reset(const _Ptr_base<_Ty2>& _Other)
08.8         {    // release resource and take ownership of _Other._Ptr
09.9         _Reset(_Other._Ptr, _Other._Rep);
10.10         }
11.11
12.12     template<class _Ty2>
13.13         void _Reset(const _Ptr_base<_Ty2>& _Other, bool _Throw)
14.14         {    // release resource and take ownership from weak_ptr _Other._Ptr
15.15         _Reset(_Other._Ptr, _Other._Rep, _Throw);
16.16         }
17.17
18.18     template<class _Ty2>
19.19         void _Reset(const _Ptr_base<_Ty2>& _Other, const _Static_tag&)
20.20         {    // release resource and take ownership of _Other._Ptr
21.21         _Reset(static_cast<_Elem *>(_Other._Ptr), _Other._Rep);
22.22         }
23.23
24.24     template<class _Ty2>
25.25         void _Reset(const _Ptr_base<_Ty2>& _Other, const _Const_tag&)
26.26         {    // release resource and take ownership of _Other._Ptr
27.27         _Reset(const_cast<_Elem *>(_Other._Ptr), _Other._Rep);
28.28         }
29.29
30.30     template<class _Ty2>
31.31         void _Reset(const _Ptr_base<_Ty2>& _Other, const _Dynamic_tag&)
32.32         {    // release resource and take ownership of _Other._Ptr
33.33         _Elem *_Ptr = dynamic_cast<_Elem *>(_Other._Ptr);
34.34         if (_Ptr)
35.35             _Reset(_Ptr, _Other._Rep);
36.36         else
37.37             _Reset();
38.38         }
39.39
40.40     template<class _Ty2>
41.41         void _Reset(auto_ptr<_Ty2>&& _Other)
42.42         {    // release resource and take _Other.get()
43.43         _Ty2 *_Px = _Other.get();
44.44         _Reset0(_Px, new _Ref_count<_Elem>(_Px));
45.45         _Other.release();
46.46         _Enable_shared(_Px, _Rep);
47.47         }
48.48
49.49  #if _HAS_CPP0X
50.50     template<class _Ty2>
51.51         void _Reset(_Ty *_Ptr, const _Ptr_base<_Ty2>& _Other)
52.52         {    // release resource and alias _Ptr with _Other_rep
53.53         _Reset(_Ptr, _Other._Rep);
54.54         }
55.55  #endif /* _HAS_CPP0X */
56.56
57.57     void _Reset(_Ty *_Other_ptr, _Ref_count_base *_Other_rep)
58.58         {    // release resource and take _Other_ptr through _Other_rep
59.59         if (_Other_rep)
60.60             _Other_rep->_Incref();
61.61         _Reset0(_Other_ptr, _Other_rep);
62.62         }
63.63
64.64     void _Reset(_Ty *_Other_ptr, _Ref_count_base *_Other_rep, bool _Throw)
65.65         {    // take _Other_ptr through _Other_rep from weak_ptr if not expired
66.66             // otherwise, leave in default state if !_Throw,
67.67             // otherwise throw exception
68.68         if (_Other_rep && _Other_rep->_Incref_nz())
69.69             _Reset0(_Other_ptr, _Other_rep);
70.70         else if (_Throw)
71.71             _THROW_NCEE(bad_weak_ptr, 0);
72.72         }
View Code

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

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

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

01.1 void _Resetw()
02.2         {    // release weak reference to resource
03.3         _Resetw((_Elem *)0, 0);
04.4         }
05.5
06.6     template<class _Ty2>
07.7         void _Resetw(const _Ptr_base<_Ty2>& _Other)
08.8         {    // release weak reference to resource and take _Other._Ptr
09.9         _Resetw(_Other._Ptr, _Other._Rep);
10.10         }
11.11
12.12     template<class _Ty2>
13.13         void _Resetw(const _Ty2 *_Other_ptr, _Ref_count_base *_Other_rep)
14.14         {    // point to _Other_ptr through _Other_rep
15.15         _Resetw(const_cast<_Ty2*>(_Other_ptr), _Other_rep);
16.16         }
17.17
18.18     template<class _Ty2>
19.19         void _Resetw(_Ty2 *_Other_ptr, _Ref_count_base *_Other_rep)
20.20         {    // point to _Other_ptr through _Other_rep
21.21         if (_Other_rep)
22.22             _Other_rep->_Incwref();
23.23         if (_Rep != 0)
24.24             _Rep->_Decwref();
25.25         _Rep = _Other_rep;
26.26         _Ptr = _Other_ptr;
27.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))

01.1   void _Decref()
02.2         {    // decrement use count
03.3         if (_MT_DECR(_Ignored, _Uses) == 0)
04.4             {    // destroy managed resource, decrement weak reference count
05.5             _Destroy();
06.6             _Decwref();
07.7             }
08.8         }
09.9
10.10     void _Decwref()
11.11         {    // decrement weak reference count
12.12         if (_MT_DECR(_Ignored, _Weaks) == 0)
13.13             _Delete_this();
14.14         }

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

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

01.1   void _Incref()
02.2         {    // increment use count
03.3         _MT_INCR(_Ignored, _Uses);
04.4         }
05.5
06.6     void _Incwref()
07.7         {    // increment weak reference count
08.8         _MT_INCR(_Ignored, _Weaks);
09.9         }

再补上 _Ref_count_base 的构造函数

1.1   _Ref_count_base()
2.2         {    // construct
3.3         _Init_atomic_counter(_Uses, 1);
4.4         _Init_atomic_counter(_Weaks, 1);
5.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。

01.1 bool _Incref_nz()
02.2         {    // increment use count if not zero, return true if successful
03.3         for (; ; )
04.4             {    // loop until state is known
05.5  #if defined(_M_IX86) || defined(_M_X64) || defined(_M_CEE_PURE)
06.6             _Atomic_integral_t _Count =
07.7                 static_cast<volatile _Atomic_counter_t&>(_Uses);
08.8
09.9             if (_Count == 0)
10.10                 return (false);
11.11
12.12             if (static_cast<_Atomic_integral_t>(_InterlockedCompareExchange(
13.13                     reinterpret_cast<volatile long *>(&_Uses),
14.14                     _Count + 1, _Count)) == _Count)
15.15                 return (true);
16.16  #else /* defined(_M_IX86) || defined(_M_X64) || defined(_M_CEE_PURE) */
17.17             _Atomic_integral_t _Count =
18.18                 _Load_atomic_counter_explicit(_Uses, memory_order_relaxed);
19.19
20.20             if (_Count == 0)
21.21                 return (false);
22.22
23.23             if (_Compare_increment_atomic_counter_explicit(
24.24                     _Uses, _Count, memory_order_relaxed))
25.25                 return (true);
26.26  #endif /* defined(_M_IX86) || defined(_M_X64) || defined(_M_CEE_PURE) */
27.27             }
28.28         }

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

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

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

1.1   void swap(_Myt& _Other) _NOEXCEPT
2.2         {    // swap pointers
3.3         this->_Swap(_Other);
4.4         }

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

1.1   void _Swap(_Ptr_base& _Right)
2.2         {    // swap pointers
3.3         _STD swap(_Rep, _Right._Rep);
4.4         _STD swap(_Ptr, _Right._Ptr);
5.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 这样的可变操作进行访问)(即使这些实 例是拷贝,而且共享下层的引用计数)。
任何其它的同时访问的结果会导致未定义行为。”

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
智能指针
为什么多线程读写 shared_ptr 要加锁?
c++11之weak
C++11智能指针源码分析
weak_ptr 类模板
C 11中智能指针的原理、使用、实现
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服