1、前言
本文僅對C++智能指針shared_ptr、weak_ptr源碼進(jìn)行解析,需要讀者有一定的C++基礎(chǔ)并且對智能指針有所了解,本文并不對智能指針的使用方法、使用場景、效率等方面進(jìn)行闡述分析,這些知識(shí)需自行查閱相關(guān)書籍去了解
2、源碼準(zhǔn)備
本文是基于gcc-4.9.0的源代碼進(jìn)行分析,shared_ptr和weak_ptr是C++11才加入標(biāo)準(zhǔn)的,所以低版本的gcc源碼是沒有shared_ptr和weak_ptr的,建議選擇4.9.0或更新的版本去學(xué)習(xí),不同版本的gcc源碼差異應(yīng)該不小,但是原理和設(shè)計(jì)思想的一樣的,下面給出源碼下載地址
http://ftp.gnu.org/gnu/gcc
3、智能指針概念
智能指針(Smart pointers)是存儲(chǔ)“指向動(dòng)態(tài)分配(在堆上)的對象的指針”的對象。也就是說,智能指針其實(shí)是個(gè)對象。不過它的行為很像C++的內(nèi)建指針,只是它們可以在適當(dāng)?shù)臅r(shí)候自動(dòng)刪除它們所指向的對象。智能指針在面對異常時(shí)有非常顯著的作用,它們可以確保動(dòng)態(tài)分配對象的完全析構(gòu)。它們還可以用于跟蹤多主人共享的動(dòng)態(tài)分配對象。在概念上,智能指針可以看作擁有它所指向的對象,并因此在對象不再需要時(shí)負(fù)責(zé)將它刪除。
4、源碼解析
4.1、shared_ptr解析
4.1.1、shared_ptr
shared_ptr
位于libstdc++-v3\include\bits\shared_ptr.h
中
1
2
3
4
5
6
7
8
9
10
11
12
13
|
template < typename _Tp> class shared_ptr : public __shared_ptr<_Tp> { public : ... // 構(gòu)造函數(shù) template < typename _Tp1> explicit shared_ptr(_Tp1* __p) :__shared_ptr<_Tp>(__p) { } ... }; |
由于源代碼過長,這里就只貼出其中一部分進(jìn)行分析:
- 該類沒有類成員
-
該類繼承于
__shared_ptr
,構(gòu)造函數(shù)也只是調(diào)用了__shared_ptr
的構(gòu)造函數(shù)而已,將接管的普通指針傳遞給__shared_ptr
-
該類沒有重載
*
和->
運(yùn)算符,從這點(diǎn)看shared_ptr
似乎無法實(shí)現(xiàn)普通指針的功能,推測這兩個(gè)運(yùn)算符的重載是在父類__shared_ptr
實(shí)現(xiàn)的 -
該類沒有析構(gòu)函數(shù),從智能指針最終會(huì)自動(dòng)釋放內(nèi)存的特性來看,釋放工作肯定不是在該類進(jìn)行了,接下來分析父類
__shared_ptr
的實(shí)現(xiàn)
4.1.2、__shared_ptr
__shared_ptr
位于libstdc++-v3\include\bits\shared_ptr_base.h
中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
template < typename _Tp, _Lock_policy _Lp> class __shared_ptr { public : typedef _Tp element_type; ... // 構(gòu)造函數(shù) template < typename _Tp1> explicit __shared_ptr(_Tp1* __p) :_M_ptr(__p), _M_refcount(__p) { __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>) static_assert( !is_void<_Tp>::value, "incomplete type" ); static_assert( sizeof (_Tp1) > 0, "incomplete type" ); __enable_shared_from_this_helper(_M_refcount, __p, __p); } // 析構(gòu)函數(shù) ~__shared_ptr() = default ; typename std::add_lvalue_reference<_Tp>::type operator*() const noexcept { _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0); return *_M_ptr; } _Tp* operator->() const noexcept { _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0); return _M_ptr; } ... private : _Tp* _M_ptr; // Contained pointer. __shared_count<_Lp> _M_refcount; // Reference counter. }; |
同樣的,源代碼比較長且不是分析的重點(diǎn),所以只貼出一部分進(jìn)行分析:
可以看到里面有兩個(gè)類成員:_M_ptr
(由智能指針接管的普通指針)、_M_refcount
(引用計(jì)數(shù)器,類型為__shared_count
)
-
從構(gòu)造函數(shù)看,
_M_ptr
獲得了接管的普通指針的值,而_M_refcount
的構(gòu)造也同樣需要這個(gè)值 -
重載了
*
和->
運(yùn)算符,由shared_ptr
繼承使用,使得智能指針最終能擁有和普通指針一樣行為,盡管智能指針本質(zhì)上是一個(gè)對象 -
從析構(gòu)函數(shù)來看,里面啥也沒做,說明接管的普通指針也不是在這里釋放的,所以有可能是由
_M_refcount
來完成釋放內(nèi)存這個(gè)工作,下面分析__shared_count
的實(shí)現(xiàn)
4.1.3、__shared_count
__shared_count
位于libstdc++-v3\include\bits\shared_ptr_base.h
中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
template <_Lock_policy _Lp> class __shared_count { public : constexpr __shared_count() noexcept : _M_pi(0) { } template < typename _Ptr> explicit __shared_count(_Ptr __p) : _M_pi(0) { __try { _M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p); } __catch(...) { delete __p; __throw_exception_again; } } template < typename _Ptr, typename _Deleter> __shared_count(_Ptr __p, _Deleter __d) :__shared_count(__p, std::move(__d), allocator< void >()) { } template < typename _Ptr, typename _Deleter, typename _Alloc> __shared_count(_Ptr __p, _Deleter __d, _Alloc __a) :_M_pi(0) { typedef _Sp_counted_deleter<_Ptr, _Deleter, _Alloc, _Lp> _Sp_cd_type; typedef typename allocator_traits<_Alloc>:: template rebind_traits<_Sp_cd_type> _Alloc_traits; typename _Alloc_traits::allocator_type __a2(__a); _Sp_cd_type* __mem = 0; __try { __mem = _Alloc_traits::allocate(__a2, 1); _Alloc_traits::construct(__a2, __mem, __p, std::move(__d), std::move(__a)); _M_pi = __mem; } __catch(...) { __d(__p); // Call _Deleter on __p. if (__mem) _Alloc_traits::deallocate(__a2, __mem, 1); __throw_exception_again; } } template < typename _Tp, typename _Alloc, typename ... _Args> __shared_count(_Sp_make_shared_tag, _Tp*, const _Alloc& __a, _Args&&... __args) :_M_pi(0) { typedef _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> _Sp_cp_type; typedef typename allocator_traits<_Alloc>:: template rebind_traits<_Sp_cp_type> _Alloc_traits; typename _Alloc_traits::allocator_type __a2(__a); _Sp_cp_type* __mem = _Alloc_traits::allocate(__a2, 1); __try { _Alloc_traits::construct(__a2, __mem, std::move(__a), std::forward<_Args>(__args)...); _M_pi = __mem; } __catch(...) { _Alloc_traits::deallocate(__a2, __mem, 1); __throw_exception_again; } } template < typename _Tp, typename _Del> explicit __shared_count(std::unique_ptr<_Tp, _Del>&& __r) :_M_pi(0) { using _Ptr = typename unique_ptr<_Tp, _Del>::pointer; using _Del2 = typename conditional<is_reference<_Del>::value, reference_wrapper< typename remove_reference<_Del>::type>, _Del>::type; using _Sp_cd_type = _Sp_counted_deleter<_Ptr, _Del2, allocator< void >, _Lp>; using _Alloc = allocator<_Sp_cd_type>; using _Alloc_traits = allocator_traits<_Alloc>; _Alloc __a; _Sp_cd_type* __mem = _Alloc_traits::allocate(__a, 1); _Alloc_traits::construct(__a, __mem, __r.release(), __r.get_deleter()); // non-throwing _M_pi = __mem; } explicit __shared_count( const __weak_count<_Lp>& __r); explicit __shared_count( const __weak_count<_Lp>& __r, std::nothrow_t); ~__shared_count() noexcept { if (_M_pi != nullptr) _M_pi->_M_release(); } __shared_count( const __shared_count& __r) noexcept :_M_pi(__r._M_pi) { if (_M_pi != 0) _M_pi->_M_add_ref_copy(); } __shared_count& operator=( const __shared_count& __r) noexcept { _Sp_counted_base<_Lp>* __tmp = __r._M_pi; if (__tmp != _M_pi) { if (__tmp != 0) __tmp->_M_add_ref_copy(); if (_M_pi != 0) _M_pi->_M_release(); _M_pi = __tmp; } return * this ; } void _M_swap(__shared_count& __r) noexcept { _Sp_counted_base<_Lp>* __tmp = __r._M_pi; __r._M_pi = _M_pi; _M_pi = __tmp; } long _M_get_use_count() const noexcept { return _M_pi != 0 ? _M_pi->_M_get_use_count() : 0; } bool _M_unique() const noexcept { return this ->_M_get_use_count() == 1; } void * _M_get_deleter( const std::type_info& __ti) const noexcept { return _M_pi ? _M_pi->_M_get_deleter(__ti) : nullptr; } bool _M_less( const __shared_count& __rhs) const noexcept { return std::less<_Sp_counted_base<_Lp>*>()( this ->_M_pi, __rhs._M_pi); } bool _M_less( const __weak_count<_Lp>& __rhs) const noexcept { return std::less<_Sp_counted_base<_Lp>*>()( this ->_M_pi, __rhs._M_pi); } friend inline bool operator==( const __shared_count& __a, const __shared_count& __b) noexcept { return __a._M_pi == __b._M_pi; } private : friend class __weak_count<_Lp>; _Sp_counted_base<_Lp>* _M_pi; } |
從源代碼可以獲得以下幾點(diǎn)信息:
有一個(gè)類成員:_M_pi
(計(jì)數(shù)器,類型為_Sp_counted_base
)
-
只有構(gòu)造函數(shù)為
_M_pi
分配了內(nèi)存,并且該類并沒有直接持有從前面一直傳遞過來的那個(gè)普通指針,而是繼續(xù)將其傳遞給_M_pi
,所以內(nèi)存的釋放也不是直接在該類進(jìn)行的。 -
拷貝構(gòu)造函數(shù)沒有分配內(nèi)容,而是把拷貝對象的
_M_pi
直接拿過來了,有點(diǎn)類似于淺拷貝的意思,然后調(diào)用了_M_pi
的_M_add_ref_copy
方法(后面會(huì)講),增加了一次引用計(jì)數(shù)。賦值函數(shù)也是同樣的道理,但是由于賦值函數(shù)的特殊性(當(dāng)賦值對象原先就存在時(shí)調(diào)用賦值函數(shù),否則調(diào)用拷貝構(gòu)造函數(shù)),要先調(diào)用_M_pi
的_M_release
方法(后面會(huì)講)將自己持有的內(nèi)存釋放掉,其余操作和拷貝構(gòu)造函數(shù)是一樣的 -
從析構(gòu)函數(shù)中可以看到,里面并沒有直接釋放掉為
_M_pi
分配的內(nèi)存,而是調(diào)用了_M_pi
的_M_release
方法,可以大概猜測是通過_M_release
方法釋放了_M_pi
的內(nèi)存(delete this指針,后面會(huì)講) -
由于
__shared_count
里面的方法都是借助_M_pi
實(shí)現(xiàn)的,并且到這里都還沒有見到釋放那個(gè)普通指針的代碼,所以還是得繼續(xù)看_M_pi
究竟做了什么工作,接下來繼續(xù)看_Sp_counted_base
的實(shí)現(xiàn)
4.1.4、_Sp_counted_base
_Sp_counted_base
位于libstdc++-v3\include\bits\shared_ptr_base.h
中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
template <_Lock_policy _Lp = __default_lock_policy> class _Sp_counted_base : public _Mutex_base<_Lp> { public : _Sp_counted_base() noexcept : _M_use_count(1), _M_weak_count(1) { } virtual ~_Sp_counted_base() noexcept { } virtual void _M_dispose() noexcept = 0; virtual void _M_destroy() noexcept { delete this ; } virtual void * _M_get_deleter( const std::type_info&) noexcept = 0; void _M_add_ref_copy() { __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); } void _M_add_ref_lock(); bool _M_add_ref_lock_nothrow(); void _M_release() noexcept { _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_use_count); if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1) { _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_use_count); _M_dispose(); if (_Mutex_base<_Lp>::_S_need_barriers) { _GLIBCXX_READ_MEM_BARRIER; _GLIBCXX_WRITE_MEM_BARRIER; } _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count); if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1) { _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count); _M_destroy(); } } } void _M_weak_add_ref() noexcept { __gnu_cxx::__atomic_add_dispatch(&_M_weak_count, 1); } void _M_weak_release() noexcept { _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count); if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1) { _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count); if (_Mutex_base<_Lp>::_S_need_barriers) { _GLIBCXX_READ_MEM_BARRIER; _GLIBCXX_WRITE_MEM_BARRIER; } _M_destroy(); } } long _M_get_use_count() const noexcept { return __atomic_load_n(&_M_use_count, __ATOMIC_RELAXED); } private : _Sp_counted_base(_Sp_counted_base const &) = delete ; _Sp_counted_base& operator=(_Sp_counted_base const &) = delete ; _Atomic_word _M_use_count; // #shared _Atomic_word _M_weak_count; // #weak + (#shared != 0) }; |
從源代碼可以獲得以下幾點(diǎn)信息:
-
有兩個(gè)類成員:
_M_use_count
(引用計(jì)數(shù))、_M_weak_count
(弱引用計(jì)數(shù)),對這兩個(gè)數(shù)的操作需要具有原子性 -
_M_release
方法是該類的關(guān)鍵,可以看到先將_M_use_count
自減1,然后判斷自減前_M_use_count
的值是否為1(無其他人引用),如果為1,則調(diào)用_M_dispose
方法(虛函數(shù),由派生類實(shí)現(xiàn),估計(jì)是釋放前面一直說的那個(gè)由智能指針接管的普通指針)。接下來將_M_weak_count
自減1,然后判斷自減前_M_weak_count
的值是否為1(無其他人引用),如果為1,則調(diào)用_M_destroy
方法,而_M_destroy
方法里面釋放了this指針,這點(diǎn)和前面的猜測一致 -
從
_M_release
可以看出,智能指針?biāo)庸艿闹羔樀尼尫艃?nèi)存工作只和_M_use_count
有關(guān),當(dāng)_M_use_count
減完時(shí)就會(huì)將其釋放了,而_M_weak_count
也是有作用的,他負(fù)責(zé)釋放_Sp_counted_base
本身,這也就是為什么weak_ptr
可以保證智能指針這個(gè)對象有效,但不保證智能指針?biāo)玫闹羔樣行У脑蛄耍ㄟ@點(diǎn)和shared_ptr、weak_ptr的定義是完全一致的) -
其他的方法就很簡單了,比如
_M_add_ref_copy
方法將引用計(jì)數(shù)_M_use_count
加一,_M_weak_add_ref
方法將弱引用計(jì)數(shù)_M_weak_count
加一,這個(gè)自增過程是具有原子性的,這里就不贅述了,大家可以自行看一下具體實(shí)現(xiàn)
4.1.5、_Sp_counted_ptr
_Sp_counted_ptr
位于libstdc++-v3\include\bits\shared_ptr_base.h
中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
template < typename _Ptr, _Lock_policy _Lp> class _Sp_counted_ptr final : public _Sp_counted_base<_Lp> { public : explicit _Sp_counted_ptr(_Ptr __p) noexcept : _M_ptr(__p) { } virtual void _M_dispose() noexcept { delete _M_ptr; } virtual void _M_destroy() noexcept { delete this ; } virtual void * _M_get_deleter( const std::type_info&) noexcept { return nullptr; } _Sp_counted_ptr( const _Sp_counted_ptr&) = delete ; _Sp_counted_ptr& operator=( const _Sp_counted_ptr&) = delete ; private : _Ptr _M_ptr; }; |
-
從源代碼中可以看到
_Sp_counted_ptr
是_Sp_counted_base
的派生類,并且__shared_count
在初始化_M_pi
時(shí)用的也是_Sp_counted_ptr
。 -
接著看
_M_dispose
方法的實(shí)現(xiàn),里面確實(shí)刪除了一開始shared_ptr
接管的指針,_M_destroy
方法用于釋放自己的內(nèi)存(由__shared_count調(diào)用),和前面猜想一致
4.1.6、shared_ptr總結(jié)
看完前面分析的內(nèi)容再回過頭來看,_Sp_counted_base
的_M_add_ref_copy
方法是整個(gè)流程的關(guān)鍵,它實(shí)現(xiàn)了引用計(jì)數(shù)器的增加,那么在何時(shí)調(diào)用它就是關(guān)鍵了。通過在代碼中檢索,可以查到__shared_count
的賦值構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)調(diào)用了它(其實(shí)也只有可能是這里啦,因?yàn)橹挥兴念惓蓡T有_Sp_counted_base
),這樣整個(gè)流程也就解釋通了:
-
__shared_count
的成員_M_pi
只會(huì)初始化一次(構(gòu)造函數(shù)中分配內(nèi)存初始化的) -
后面調(diào)用拷貝構(gòu)造時(shí)(這個(gè)行為由
__shared_ptr
觸發(fā),__shared_ptr
的拷貝構(gòu)造函數(shù)和賦值函數(shù)都會(huì)調(diào)用__shared_count
的拷貝構(gòu)造函數(shù)),__shared_count
只是簡單復(fù)制了_M_pi
而已,并沒有重新分配內(nèi)存,然后再調(diào)用_M_add_ref_copy
增加一次引用計(jì)數(shù),這樣就實(shí)現(xiàn)了shared_ptr
每多一份拷貝就增加一次引用計(jì)數(shù)的特性了 -
每一個(gè)
__shared_count
被析構(gòu)都會(huì)使引用計(jì)數(shù)減一,減完就將智能指針持有的資源釋放,這個(gè)前面已經(jīng)分析過了,這里就不贅述了
4.2、weak_ptr解析
4.2.1、weak_ptr
weak_ptr
位于libstdc++-v3\include\bits\shared_ptr.h
中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
template < typename _Tp> class weak_ptr : public __weak_ptr<_Tp> { public : constexpr weak_ptr() noexcept :__weak_ptr<_Tp>() { } template < typename _Tp1, typename = typename std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type> weak_ptr( const weak_ptr<_Tp1>& __r) noexcept :__weak_ptr<_Tp>(__r) { } template < typename _Tp1, typename = typename std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type> weak_ptr( const shared_ptr<_Tp1>& __r) noexcept :__weak_ptr<_Tp>(__r) { } template < typename _Tp1> weak_ptr& operator=( const weak_ptr<_Tp1>& __r) noexcept { this ->__weak_ptr<_Tp>::operator=(__r); return * this ; } template < typename _Tp1> weak_ptr& operator=( const shared_ptr<_Tp1>& __r) noexcept { this ->__weak_ptr<_Tp>::operator=(__r); return * this ; } shared_ptr<_Tp> lock() const noexcept { return shared_ptr<_Tp>(* this , std:: nothrow ); } } |
從源代碼中可以看出以下幾點(diǎn):
- 該類沒有類成員
-
從構(gòu)造函數(shù)的參數(shù)來看(無參構(gòu)造函數(shù)除外),只能使用
shared_ptr
或weak_ptr
來構(gòu)造一個(gè)weak_ptr
對象,包括賦值函數(shù)也是這樣的,這就和shared_ptr
有很大區(qū)別了,從4.1.1小節(jié)
可以看到shared_ptr
是可以使用普通指針來構(gòu)造的 -
可以調(diào)用
lock
方法來獲得一個(gè)shared_ptr
,lock
方法的實(shí)現(xiàn)后面再講
該類沒有重載*
和->
運(yùn)算符,接下來分析其父類__weak_ptr
的實(shí)現(xiàn)
4.2.2、__weak_ptr
__weak_ptr
位于libstdc++-v3\include\bits\shared_ptr_base.h
中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
template < typename _Tp, _Lock_policy _Lp> class __weak_ptr { public : typedef _Tp element_type; constexpr __weak_ptr() noexcept :_M_ptr(0) ,_M_refcount() { } __weak_ptr( const __weak_ptr&) noexcept = default ; __weak_ptr& operator=( const __weak_ptr&) noexcept = default ; ~__weak_ptr() = default ; template < typename _Tp1, typename = typename std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type> __weak_ptr( const __weak_ptr<_Tp1, _Lp>& __r) noexcept :_M_refcount(__r._M_refcount) { _M_ptr = __r.lock().get(); } template < typename _Tp1, typename = typename std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type> __weak_ptr( const __shared_ptr<_Tp1, _Lp>& __r) noexcept :_M_ptr(__r._M_ptr) ,_M_refcount(__r._M_refcount) { } template < typename _Tp1> __weak_ptr& operator=( const __weak_ptr<_Tp1, _Lp>& __r) noexcept { _M_ptr = __r.lock().get(); _M_refcount = __r._M_refcount; return * this ; } template < typename _Tp1> __weak_ptr& operator=( const __shared_ptr<_Tp1, _Lp>& __r) noexcept { _M_ptr = __r._M_ptr; _M_refcount = __r._M_refcount; return * this ; } __shared_ptr<_Tp, _Lp> lock() const noexcept { return __shared_ptr<element_type, _Lp>(* this , std:: nothrow ); } long use_count() const noexcept { return _M_refcount._M_get_use_count(); } bool expired() const noexcept { return _M_refcount._M_get_use_count() == 0; } template < typename _Tp1> bool owner_before( const __shared_ptr<_Tp1, _Lp>& __rhs) const { return _M_refcount._M_less(__rhs._M_refcount); } template < typename _Tp1> bool owner_before( const __weak_ptr<_Tp1, _Lp>& __rhs) const { return _M_refcount._M_less(__rhs._M_refcount); } void reset() noexcept { __weak_ptr().swap(* this ); } void swap(__weak_ptr& __s) noexcept { std::swap(_M_ptr, __s._M_ptr); _M_refcount._M_swap(__s._M_refcount); } private : // Used by __enable_shared_from_this. void _M_assign(_Tp* __ptr, const __shared_count<_Lp>& __refcount) noexcept { _M_ptr = __ptr; _M_refcount = __refcount; } template < typename _Tp1, _Lock_policy _Lp1> friend class __shared_ptr; template < typename _Tp1, _Lock_policy _Lp1> friend class __weak_ptr; friend class __enable_shared_from_this<_Tp, _Lp>; friend class enable_shared_from_this<_Tp>; _Tp* _M_ptr; // Contained pointer. __weak_count<_Lp> _M_refcount; // Reference counter. } |
從源代碼中可以看出以下幾點(diǎn)信息:
-
有兩個(gè)類成員:
_M_ptr
(由智能指針接管的普通指針)、_M_refcount
(弱引用計(jì)數(shù)器,類型為__weak_count) -
從構(gòu)造函數(shù)看,
_M_ptr
獲得了接管的普通指針的值,而_M_refcount
的構(gòu)造并不需要這個(gè)值了(這點(diǎn)和__shared_ptr
不一樣了),_M_refcount
只能借助其他__shared_ptr
的_M_refcount
或者__weak_ptr
的_M_refcount
來進(jìn)行構(gòu)造(注意這兩個(gè)的_M_refcount
類型不同,說明__weak_count
支持多種類型進(jìn)行構(gòu)造) - 拷貝構(gòu)造函數(shù)和賦值函數(shù)的實(shí)現(xiàn)同上
-
該類依然沒有重載
*
和->
運(yùn)算符,由于接下去已無繼承關(guān)系,所以weak_ptr
不具備普通指針的特性,無法直接使用資源,這點(diǎn)符合weak_ptr
的定義 -
既然
weak_ptr
無法直接使用資源,那他設(shè)計(jì)_M_ptr
這個(gè)成員的意圖在哪里呢?答案就是lock
方法將weak_ptr
轉(zhuǎn)換為shared_ptr
時(shí)是需要將這個(gè)指針傳遞過去的,不然連接管的指針都沒了轉(zhuǎn)換的意義也就沒了 -
析構(gòu)函數(shù)啥也沒做,因?yàn)?code>weak_ptr不持有資源,不對資源的釋放產(chǎn)生影響,接下來對
__weak_count
進(jìn)行分析
4.2.3、__weak_count
__weak_count
的實(shí)現(xiàn)位于libstdc++-v3\include\bits\shared_ptr_base.h
中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
template <_Lock_policy _Lp> class __weak_count { public : constexpr __weak_count() noexcept : _M_pi(0) { } __weak_count( const __shared_count<_Lp>& __r) noexcept :_M_pi(__r._M_pi) { if (_M_pi != 0) _M_pi->_M_weak_add_ref(); } __weak_count( const __weak_count<_Lp>& __r) noexcept :_M_pi(__r._M_pi) { if (_M_pi != 0) _M_pi->_M_weak_add_ref(); } ~__weak_count() noexcept { if (_M_pi != 0) _M_pi->_M_weak_release(); } __weak_count<_Lp>& operator=( const __shared_count<_Lp>& __r) noexcept { _Sp_counted_base<_Lp>* __tmp = __r._M_pi; if (__tmp != 0) __tmp->_M_weak_add_ref(); if (_M_pi != 0) _M_pi->_M_weak_release(); _M_pi = __tmp; return * this ; } __weak_count<_Lp>& operator=( const __weak_count<_Lp>& __r) noexcept { _Sp_counted_base<_Lp>* __tmp = __r._M_pi; if (__tmp != 0) __tmp->_M_weak_add_ref(); if (_M_pi != 0) _M_pi->_M_weak_release(); _M_pi = __tmp; return * this ; } void _M_swap(__weak_count<_Lp>& __r) noexcept { _Sp_counted_base<_Lp>* __tmp = __r._M_pi; __r._M_pi = _M_pi; _M_pi = __tmp; } long _M_get_use_count() const noexcept { return _M_pi != 0 ? _M_pi->_M_get_use_count() : 0; } bool _M_less( const __weak_count& __rhs) const noexcept { return std::less<_Sp_counted_base<_Lp>*>()( this ->_M_pi, __rhs._M_pi); } bool _M_less( const __shared_count<_Lp>& __rhs) const noexcept { return std::less<_Sp_counted_base<_Lp>*>()( this ->_M_pi, __rhs._M_pi); } friend inline bool operator==( const __weak_count& __a, const __weak_count& __b) noexcept { return __a._M_pi == __b._M_pi; } private : friend class __shared_count<_Lp>; _Sp_counted_base<_Lp>* _M_pi; } |
從源代碼可以獲得以下幾點(diǎn)信息:
-
有一個(gè)類成員:
_M_pi
(計(jì)數(shù)器,類型為_Sp_counted_base) -
仔細(xì)一看
__shared_count
里也持有這個(gè)成員,類型一模一樣,這樣也就解釋得通為什么__shared_count
和__weak_count
可以互相轉(zhuǎn)換了,轉(zhuǎn)換的方式很簡單:
__shared_count
轉(zhuǎn)換為__weak_count
的過程為:
拷貝_M_pi
,然后調(diào)用_M_weak_add_ref
方法增加一次弱引用計(jì)數(shù)__weak_count
轉(zhuǎn)換為__shared_count
的過程為:
拷貝_M_pi
,然后調(diào)用_M_add_ref_copy
方法增加一次引用計(jì)數(shù)
-
構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、賦值函數(shù)均不為
_M_pi
分配了內(nèi)存,這點(diǎn)也可以看出weak_ptr
確實(shí)是shared_ptr
的附屬品而已,自己不持有資源不控制資源 -
析構(gòu)函數(shù)中調(diào)用了
_M_pi
的_M_weak_release
方法,釋放了_M_pi
的內(nèi)存(條件滿足的情況下才會(huì)釋放) -
接下來的內(nèi)容和
3.1.4小節(jié)
還有3.1.5小節(jié)
的內(nèi)容是一樣的,這里就不贅述
4.2.4、回過頭看weak_ptr中l(wèi)ock方法的實(shí)現(xiàn)
weak_ptr
的lock
方法調(diào)用了shared_ptr
的構(gòu)造函數(shù)如下:
1
2
3
4
|
shared_ptr( const weak_ptr<_Tp>& __r, std::nothrow_t) :__shared_ptr<_Tp>(__r, std:: nothrow ) { } |
從上面的代碼可以看出調(diào)用了__shared_ptr
的構(gòu)造函數(shù),代碼如下:
1
2
3
4
5
|
__shared_ptr( const __weak_ptr<_Tp, _Lp>& __r, std::nothrow_t) :_M_refcount(__r._M_refcount, std:: nothrow ) { _M_ptr = _M_refcount._M_get_use_count() ? __r._M_ptr : nullptr; } |
可以看到此時(shí)先是使用了__weak_ptr
的_M_refcount
成員(類型為__weak_count
)來構(gòu)造__shared_ptr
的_M_refcount
成員(類型為__shared_count
),然后再判斷引用計(jì)數(shù)器是否為0,為零的話就將__shared_ptr
的_M_ptr
成員置為nullptr
,即lock
函數(shù)執(zhí)行失??;不為零的話就會(huì)正常構(gòu)建一個(gè)shared_ptr
了。
上面講的構(gòu)造_M_refcount
的方法如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
template <_Lock_policy _Lp> inline __shared_count<_Lp>::__shared_count( const __weak_count<_Lp>& __r, std::nothrow_t) :_M_pi(__r._M_pi) { if (_M_pi != nullptr) if (!_M_pi->_M_add_ref_lock_nothrow()) _M_pi = nullptr; } template <> inline bool _Sp_counted_base<_S_single>::_M_add_ref_lock_nothrow() { if (_M_use_count == 0) return false ; ++_M_use_count; return true ; } |
從上面的代碼中我們可以看到,首先__shared_count
使用__weak_count
的_M_pi
來構(gòu)建自己的_M_pi
,從前面的分析我們可以知道,在所有的shared_ptr
和weak_ptr
消亡之前,_M_pi
的內(nèi)存是不會(huì)被釋放的,所以這里就算之前的shared_ptr
已經(jīng)全部消亡(即資源已釋放),_M_pi
還是有效的(因?yàn)?code>weak_ptr還沒有消亡)。而通過判斷_M_add_ref_lock_nothrow
的返回值來確定是否要將_M_pi
置為nullptr
,可以看到判斷的條件為_M_use_count
是否為0(即判斷資源是否被釋放了)。
接下來再看一下__shared_count
的_M_get_use_count
方法,代碼如下:
1
2
|
long _M_get_use_count() const noexcept { return _M_pi != 0 ? _M_pi->_M_get_use_count() : 0; } |
代碼比較簡單,意思就是如果此時(shí)資源已經(jīng)被釋放了(對應(yīng)_M_pi
值為nullptr
),則會(huì)返回0,再回到上面第2點(diǎn)講的那里,_M_ptr
將被設(shè)置為nullptr
,即資源無效,lock
函數(shù)執(zhí)行失敗。
至此weak_ptr
的lock
方法的實(shí)現(xiàn)原理就全部講解完畢。
4.3、enable_shared_from_this解析
4.3.1、從一個(gè)典型的例子來認(rèn)識(shí)智能指針的不足之處
有時(shí)候我們需要在一個(gè)被shared_ptr
管理的對象的內(nèi)部獲取自己的shared_ptr,比如下面這個(gè)的例子:
1
2
3
4
5
6
7
8
9
10
11
12
|
class Ptr { public : void fun() { std::shared_ptr<Ptr> p( this ); std::cout << sp->use_count() << std::endl; } }; std::shared_ptr<Ptr> p= std::make_shared<Ptr>(); p->fun(); //輸出為1 |
從上面這個(gè)簡單的例子可以看到,fun
輸出的居然是1而不是2,這是為什么?倒回去4.1.2小節(jié)
可以看到,當(dāng)使用普通指針(上面的那個(gè)this)去構(gòu)造shared_ptr
時(shí),構(gòu)造出來的shared_ptr
一定是獨(dú)立的,不與其他人共享的。這樣就會(huì)出現(xiàn)一個(gè)非常嚴(yán)重的問題,那就是析構(gòu)時(shí)會(huì)導(dǎo)致對象被重復(fù)釋放, 從而引發(fā)錯(cuò)誤
4.3.2、改進(jìn)方法
現(xiàn)在明確一下我們的需求:在一個(gè)對象內(nèi)部構(gòu)造該對象的shared_ptr
時(shí),即使該對象已經(jīng)被shared_ptr
管理著,也不會(huì)造成對象被兩個(gè)獨(dú)立的智能指針管理。這就要求我們在對象內(nèi)構(gòu)造對象的智能指針時(shí),必須能識(shí)別有對象是否已經(jīng)由其他智能指針管理,智能指針的數(shù)量,并且我們創(chuàng)建智能指針后也能讓之前的智能指針感知到。當(dāng)然標(biāo)準(zhǔn)已經(jīng)也給出了解決了這個(gè)問題辦法,那就是使用接下來所提到的enable_shared_from_this
4.3.3、enable_shared_from_this解析
enable_shared_from_this
的實(shí)現(xiàn)位于libstdc++-v3\include\bits\shared_ptr.h
中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
template < typename _Tp> class enable_shared_from_this { protected : constexpr enable_shared_from_this() noexcept { } enable_shared_from_this( const enable_shared_from_this&) noexcept { } enable_shared_from_this& operator=( const enable_shared_from_this&) noexcept { return * this ; } ~enable_shared_from_this() { } public : shared_ptr<_Tp> shared_from_this() { return shared_ptr<_Tp>( this ->_M_weak_this); } shared_ptr< const _Tp> shared_from_this() const { return shared_ptr< const _Tp>( this ->_M_weak_this); } private : template < typename _Tp1> void _M_weak_assign(_Tp1* __p, const __shared_count<>& __n) const noexcept { _M_weak_this._M_assign(__p, __n); } template < typename _Tp1> friend void __enable_shared_from_this_helper( const __shared_count<>& __pn, const enable_shared_from_this* __pe, const _Tp1* __px) noexcept { if (__pe != 0) __pe->_M_weak_assign( const_cast <_Tp1*>(__px), __pn); } mutable weak_ptr<_Tp> _M_weak_this; }; |
從源代碼可以獲得以下幾點(diǎn)信息:
-
有一個(gè)類成員:
_M_weak_this
- 該類需要被繼承,被需要用智能指針管理的對象繼承
-
我們平時(shí)就是使用該類的
shared_from_this
方法的,可以看到其實(shí)現(xiàn)就是利用_M_weak_this
構(gòu)造一個(gè)shared_ptr
對象而已 -
該類并沒有直接初始化
_M_weak_this
,而是提供了_M_weak_assign
方法來構(gòu)造_M_weak_this
,其實(shí)現(xiàn)比較簡單,就是調(diào)用了weak_ptr
的_M_assign
方法 -
那么問題來了,
_M_weak_assign
方法由誰調(diào)用呢?從后面我們可以知道是由一個(gè)全局函數(shù)__enable_shared_from_this_helper
調(diào)用的,該函數(shù)有一種重載形式是enable_shared_from_this
的友元函數(shù),從上面的代碼中就可以看到了,那唯一一個(gè)友元函數(shù)就是__enable_shared_from_this_helper
,里面調(diào)用了enable_shared_from_this
的_M_weak_assign
方法。 -
而
__enable_shared_from_this_helper
函數(shù)要在哪個(gè)時(shí)間點(diǎn)使用才能達(dá)到預(yù)期的效果呢?答案當(dāng)然是在__shared_ptr
的構(gòu)造函數(shù)中調(diào)用。下面列出了__shared_ptr
部分構(gòu)造函數(shù),可以看到確實(shí)調(diào)用了__enable_shared_from_this_helper
,證實(shí)了前面的猜想
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
template < typename _Tp, _Lock_policy _Lp> class __shared_ptr { public : ... template < typename _Tp1> explicit __shared_ptr(_Tp1* __p) :_M_ptr(__p) ,_M_refcount(__p) { __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>) static_assert( !is_void<_Tp>::value, "incomplete type" ); static_assert( sizeof (_Tp1) > 0, "incomplete type" ); __enable_shared_from_this_helper(_M_refcount, __p, __p); } template < typename _Tp1, typename _Deleter> __shared_ptr(_Tp1* __p, _Deleter __d) :_M_ptr(__p) ,_M_refcount(__p, __d) { __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>) __enable_shared_from_this_helper(_M_refcount, __p, __p); } template < typename _Tp1, typename _Deleter, typename _Alloc> __shared_ptr(_Tp1* __p, _Deleter __d, _Alloc __a) :_M_ptr(__p) ,_M_refcount(__p, __d, std::move(__a)) { __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>) __enable_shared_from_this_helper(_M_refcount, __p, __p); } ... }; |
4.3.4、__enable_shared_from_this_helper解析
__enable_shared_from_this_helper
的實(shí)現(xiàn)位于libstdc++-v3\include\bits\shared_ptr_base.h
中
1
2
3
4
5
6
7
|
// Friend of enable_shared_from_this. template < typename _Tp1, typename _Tp2> void __enable_shared_from_this_helper( const __shared_count<>&, const enable_shared_from_this<_Tp1>*, const _Tp2*) noexcept; template <_Lock_policy _Lp> inline void __enable_shared_from_this_helper( const __shared_count<_Lp>&, ...) noexcept { } |
這里有必要再看一下__enable_shared_from_this_helper
函數(shù)的實(shí)現(xiàn),有兩種形式,第一種就是上面提到過的那個(gè)enable_shared_from_this
的友元函數(shù),而第二種重載形式里面啥都沒有干。為什么需要重載這兩個(gè)函數(shù)呢?答案很簡單,當(dāng)我們一個(gè)類繼承了enable_shared_from_this
之后,這個(gè)類肯定可以轉(zhuǎn)換為enable_shared_from_this
類型了,此時(shí)在__shared_ptr
中調(diào)用的__enable_shared_from_this_helper
就是上面第一種情況了,這種情況下就可以使用shared_from_this
函數(shù)了;反之,當(dāng)類沒有繼承enable_shared_from_this
時(shí),就是調(diào)用第二中形式的__enable_shared_from_this_helper
,此時(shí)也就不能使用shared_from_this
函數(shù)了。
至此,為什么在使用shared_from_this
前,對應(yīng)的類需要繼承enable_shared_from_this
的原因也就全部揭曉了。
5、總結(jié)
本文先是簡單介紹了C++智能指針的定義,然后通過對源碼進(jìn)行詳細(xì)分析,我們了解了shared_ptr
、weak_ptr
以及enable_shared_from_this
的實(shí)現(xiàn)原理。源代碼內(nèi)容并不是很復(fù)雜,沒有用到什么很高深的語法糖,但是閱讀起來非常繞(因?yàn)檫@三個(gè)類的關(guān)聯(lián)錯(cuò)綜復(fù)雜),這就需要我們有耐心地一步一步去深入學(xué)習(xí)。
最后,如果大家覺得本文寫得好的話麻煩點(diǎn)贊收藏關(guān)注一下謝謝,也可以關(guān)注該專欄,以后會(huì)有更多優(yōu)質(zhì)文章輸出的。
原文鏈接:https://blog.csdn.net/weixin_43798887/article/details/116464334