一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - C/C++ - C++11中的智能指針shared_ptr、weak_ptr源碼解析

C++11中的智能指針shared_ptr、weak_ptr源碼解析

2022-01-12 14:10彼 方 C/C++

本文是基于gcc-4.9.0的源代碼進(jìn)行分析,shared_ptr和weak_ptr是C++11才加入標(biāo)準(zhǔn)的,僅對C++智能指針shared_ptr、weak_ptr源碼進(jìn)行解析,需要讀者有一定的C++基礎(chǔ)并且對智能指針有所了解

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)行分析:

  1. 該類沒有類成員
  2. 該類繼承于__shared_ptr,構(gòu)造函數(shù)也只是調(diào)用了__shared_ptr的構(gòu)造函數(shù)而已,將接管的普通指針傳遞給__shared_ptr
  3. 該類沒有重載*->運(yùn)算符,從這點(diǎn)看shared_ptr似乎無法實(shí)現(xiàn)普通指針的功能,推測這兩個(gè)運(yùn)算符的重載是在父類__shared_ptr實(shí)現(xiàn)的
  4. 該類沒有析構(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)

  1. 從構(gòu)造函數(shù)看,_M_ptr獲得了接管的普通指針的值,而_M_refcount的構(gòu)造也同樣需要這個(gè)值
  2. 重載了*->運(yùn)算符,由shared_ptr繼承使用,使得智能指針最終能擁有和普通指針一樣行為,盡管智能指針本質(zhì)上是一個(gè)對象
  3. 從析構(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)

  1. 只有構(gòu)造函數(shù)為_M_pi分配了內(nèi)存,并且該類并沒有直接持有從前面一直傳遞過來的那個(gè)普通指針,而是繼續(xù)將其傳遞給_M_pi,所以內(nèi)存的釋放也不是直接在該類進(jìn)行的。
  2. 拷貝構(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ù)是一樣的
  3. 從析構(gòu)函數(shù)中可以看到,里面并沒有直接釋放掉為_M_pi分配的內(nèi)存,而是調(diào)用了_M_pi_M_release方法,可以大概猜測是通過_M_release方法釋放了_M_pi的內(nèi)存(delete this指針,后面會(huì)講)
  4. 由于__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)信息:

  1. 有兩個(gè)類成員:_M_use_count(引用計(jì)數(shù))、_M_weak_count(弱引用計(jì)數(shù)),對這兩個(gè)數(shù)的操作需要具有原子性
  2. _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)和前面的猜測一致
  3. _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的定義是完全一致的)
  4. 其他的方法就很簡單了,比如_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;
};
  1. 從源代碼中可以看到_Sp_counted_ptr_Sp_counted_base的派生類,并且__shared_count在初始化_M_pi時(shí)用的也是_Sp_counted_ptr。
  2. 接著看_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è)流程也就解釋通了:

  1. __shared_count的成員_M_pi只會(huì)初始化一次(構(gòu)造函數(shù)中分配內(nèi)存初始化的)
  2. 后面調(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ù)的特性了
  3. 每一個(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):

  1. 該類沒有類成員
  2. 從構(gòu)造函數(shù)的參數(shù)來看(無參構(gòu)造函數(shù)除外),只能使用shared_ptrweak_ptr來構(gòu)造一個(gè)weak_ptr對象,包括賦值函數(shù)也是這樣的,這就和shared_ptr有很大區(qū)別了,從4.1.1小節(jié)可以看到shared_ptr是可以使用普通指針來構(gòu)造的
  3. 可以調(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)信息:

  1. 有兩個(gè)類成員:_M_ptr(由智能指針接管的普通指針)、_M_refcount(弱引用計(jì)數(shù)器,類型為__weak_count)
  2. 從構(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)造)
  3. 拷貝構(gòu)造函數(shù)和賦值函數(shù)的實(shí)現(xiàn)同上
  4. 該類依然沒有重載*->運(yùn)算符,由于接下去已無繼承關(guān)系,所以weak_ptr不具備普通指針的特性,無法直接使用資源,這點(diǎn)符合weak_ptr的定義
  5. 既然weak_ptr無法直接使用資源,那他設(shè)計(jì)_M_ptr這個(gè)成員的意圖在哪里呢?答案就是lock方法將weak_ptr轉(zhuǎn)換為shared_ptr時(shí)是需要將這個(gè)指針傳遞過去的,不然連接管的指針都沒了轉(zhuǎn)換的意義也就沒了
  6. 析構(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)信息:

  1. 有一個(gè)類成員:_M_pi(計(jì)數(shù)器,類型為_Sp_counted_base)
  2. 仔細(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ù)
  1. 構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、賦值函數(shù)均不為_M_pi分配了內(nèi)存,這點(diǎn)也可以看出weak_ptr確實(shí)是shared_ptr的附屬品而已,自己不持有資源不控制資源
  2. 析構(gòu)函數(shù)中調(diào)用了_M_pi_M_weak_release方法,釋放了_M_pi的內(nèi)存(條件滿足的情況下才會(huì)釋放)
  3. 接下來的內(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_ptrlock方法調(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_ptrweak_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_ptrlock方法的實(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)信息:

  1. 有一個(gè)類成員:_M_weak_this
  2. 該類需要被繼承,被需要用智能指針管理的對象繼承
  3. 我們平時(shí)就是使用該類的shared_from_this方法的,可以看到其實(shí)現(xiàn)就是利用_M_weak_this構(gòu)造一個(gè)shared_ptr對象而已
  4. 該類并沒有直接初始化_M_weak_this,而是提供了_M_weak_assign方法來構(gòu)造_M_weak_this,其實(shí)現(xiàn)比較簡單,就是調(diào)用了weak_ptr_M_assign方法
  5. 那么問題來了,_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方法。
  6. __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_ptrweak_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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 黄色a∨| 免费视频 | 国产第7页 | 色综合伊人色综合网亚洲欧洲 | mm131亚洲精品久久 | 俄罗斯激情性孕妇孕交大全 | 久久久久久88色偷偷 | 99青青青精品视频在线 | 亚洲成人综合在线 | 精新精新国产自在现拍 | 日本四虎影院 | 国产喂奶300部 | 亚洲欧美一区二区三区在饯 | 九九99热久久999精品 | 久久久精品日本一区二区三区 | 免费看男女污污完整版 | 亚洲天堂一区二区在线观看 | 日本指交| 国产这里有精品 | 99热这里只有精品在线观看 | 亚洲国产成人久久精品hezyo | 日本免费在线观看视频 | 九九精品99久久久香蕉 | 万域之王动漫在线观看全集免费播放 | 毛片免费视频观看 | 男人疯狂擦进女人下面 | 日本三级免费看 | 色综合天天综合网国产人 | 免费在线观看中文字幕 | 男人扒开 | 国产成人h综合亚洲欧美在线 | 亚洲成aⅴ人片在线 | 含羞草国产亚洲精品岁国产精品 | 青草视频免费观看在线观看 | 国产普通话对白露脸流出 | 日本在线视 | 久久亚洲精品成人 | 免费全看男女拍拍拍的视频 | 欧美视频一区二区专区 | 国产成人综合精品 | 射18p|