国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

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

服務器之家 - 編程語言 - 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的源代碼進行分析,shared_ptr和weak_ptr是C++11才加入標準的,僅對C++智能指針shared_ptr、weak_ptr源碼進行解析,需要讀者有一定的C++基礎并且對智能指針有所了解

1、前言

本文僅對C++智能指針shared_ptr、weak_ptr源碼進行解析,需要讀者有一定的C++基礎并且對智能指針有所了解,本文并不對智能指針的使用方法、使用場景、效率等方面進行闡述分析,這些知識需自行查閱相關書籍去了解

2、源碼準備

本文是基于gcc-4.9.0的源代碼進行分析,shared_ptr和weak_ptr是C++11才加入標準的,所以低版本的gcc源碼是沒有shared_ptr和weak_ptr的,建議選擇4.9.0或更新的版本去學習,不同版本的gcc源碼差異應該不小,但是原理和設計思想的一樣的,下面給出源碼下載地址
http://ftp.gnu.org/gnu/gcc

3、智能指針概念

智能指針(Smart pointers)是存儲“指向動態分配(在堆上)的對象的指針”的對象。也就是說,智能指針其實是個對象。不過它的行為很像C++的內建指針,只是它們可以在適當的時候自動刪除它們所指向的對象。智能指針在面對異常時有非常顯著的作用,它們可以確保動態分配對象的完全析構。它們還可以用于跟蹤多主人共享的動態分配對象。在概念上,智能指針可以看作擁有它所指向的對象,并因此在對象不再需要時負責將它刪除。

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:
...
    // 構造函數
    template<typename _Tp1>
    explicit shared_ptr(_Tp1* __p)
        :__shared_ptr<_Tp>(__p)
    {
    }
...
};

由于源代碼過長,這里就只貼出其中一部分進行分析:

  1. 該類沒有類成員
  2. 該類繼承于__shared_ptr,構造函數也只是調用了__shared_ptr的構造函數而已,將接管的普通指針傳遞給__shared_ptr
  3. 該類沒有重載*->運算符,從這點看shared_ptr似乎無法實現普通指針的功能,推測這兩個運算符的重載是在父類__shared_ptr實現的
  4. 該類沒有析構函數,從智能指針最終會自動釋放內存的特性來看,釋放工作肯定不是在該類進行了,接下來分析父類__shared_ptr的實現

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;
...
    // 構造函數
    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);
    }
 
    // 析構函數
    ~__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.
};

同樣的,源代碼比較長且不是分析的重點,所以只貼出一部分進行分析:

可以看到里面有兩個類成員:_M_ptr(由智能指針接管的普通指針)、_M_refcount(引用計數器,類型為__shared_count)

  1. 從構造函數看,_M_ptr獲得了接管的普通指針的值,而_M_refcount的構造也同樣需要這個值
  2. 重載了*->運算符,由shared_ptr繼承使用,使得智能指針最終能擁有和普通指針一樣行為,盡管智能指針本質上是一個對象
  3. 從析構函數來看,里面啥也沒做,說明接管的普通指針也不是在這里釋放的,所以有可能是由_M_refcount來完成釋放內存這個工作,下面分析__shared_count的實現

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;
}

從源代碼可以獲得以下幾點信息:

有一個類成員:_M_pi(計數器,類型為_Sp_counted_base)

  1. 只有構造函數為_M_pi分配了內存,并且該類并沒有直接持有從前面一直傳遞過來的那個普通指針,而是繼續將其傳遞給_M_pi,所以內存的釋放也不是直接在該類進行的。
  2. 拷貝構造函數沒有分配內容,而是把拷貝對象的_M_pi直接拿過來了,有點類似于淺拷貝的意思,然后調用了_M_pi_M_add_ref_copy方法(后面會講),增加了一次引用計數。賦值函數也是同樣的道理,但是由于賦值函數的特殊性(當賦值對象原先就存在時調用賦值函數,否則調用拷貝構造函數),要先調用_M_pi_M_release方法(后面會講)將自己持有的內存釋放掉,其余操作和拷貝構造函數是一樣的
  3. 從析構函數中可以看到,里面并沒有直接釋放掉為_M_pi分配的內存,而是調用了_M_pi_M_release方法,可以大概猜測是通過_M_release方法釋放了_M_pi的內存(delete this指針,后面會講)
  4. 由于__shared_count里面的方法都是借助_M_pi實現的,并且到這里都還沒有見到釋放那個普通指針的代碼,所以還是得繼續看_M_pi究竟做了什么工作,接下來繼續看_Sp_counted_base的實現

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)
};

從源代碼可以獲得以下幾點信息:

  1. 有兩個類成員:_M_use_count(引用計數)、_M_weak_count(弱引用計數),對這兩個數的操作需要具有原子性
  2. _M_release方法是該類的關鍵,可以看到先將_M_use_count自減1,然后判斷自減前_M_use_count的值是否為1(無其他人引用),如果為1,則調用_M_dispose方法(虛函數,由派生類實現,估計是釋放前面一直說的那個由智能指針接管的普通指針)。接下來將_M_weak_count自減1,然后判斷自減前_M_weak_count的值是否為1(無其他人引用),如果為1,則調用_M_destroy方法,而_M_destroy方法里面釋放了this指針,這點和前面的猜測一致
  3. _M_release可以看出,智能指針所接管的指針的釋放內存工作只和_M_use_count有關,當_M_use_count減完時就會將其釋放了,而_M_weak_count也是有作用的,他負責釋放_Sp_counted_base本身,這也就是為什么weak_ptr可以保證智能指針這個對象有效,但不保證智能指針所引用的指針有效的原因了(這點和shared_ptr、weak_ptr的定義是完全一致的)
  4. 其他的方法就很簡單了,比如_M_add_ref_copy方法將引用計數_M_use_count加一,_M_weak_add_ref方法將弱引用計數_M_weak_count加一,這個自增過程是具有原子性的,這里就不贅述了,大家可以自行看一下具體實現

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時用的也是_Sp_counted_ptr
  2. 接著看_M_dispose方法的實現,里面確實刪除了一開始shared_ptr接管的指針,_M_destroy方法用于釋放自己的內存(由__shared_count調用),和前面猜想一致

4.1.6、shared_ptr總結

看完前面分析的內容再回過頭來看,_Sp_counted_base_M_add_ref_copy方法是整個流程的關鍵,它實現了引用計數器的增加,那么在何時調用它就是關鍵了。通過在代碼中檢索,可以查到__shared_count的賦值構造函數和拷貝構造函數調用了它(其實也只有可能是這里啦,因為只有它的類成員有_Sp_counted_base),這樣整個流程也就解釋通了:

  1. __shared_count的成員_M_pi只會初始化一次(構造函數中分配內存初始化的)
  2. 后面調用拷貝構造時(這個行為由__shared_ptr觸發,__shared_ptr的拷貝構造函數和賦值函數都會調用__shared_count的拷貝構造函數),__shared_count只是簡單復制了_M_pi而已,并沒有重新分配內存,然后再調用_M_add_ref_copy增加一次引用計數,這樣就實現了shared_ptr每多一份拷貝就增加一次引用計數的特性了
  3. 每一個__shared_count被析構都會使引用計數減一,減完就將智能指針持有的資源釋放,這個前面已經分析過了,這里就不贅述了

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);
    }
}

從源代碼中可以看出以下幾點:

  1. 該類沒有類成員
  2. 從構造函數的參數來看(無參構造函數除外),只能使用shared_ptrweak_ptr來構造一個weak_ptr對象,包括賦值函數也是這樣的,這就和shared_ptr有很大區別了,從4.1.1小節可以看到shared_ptr是可以使用普通指針來構造的
  3. 可以調用lock方法來獲得一個shared_ptrlock方法的實現后面再講

該類沒有重載*->運算符,接下來分析其父類__weak_ptr的實現

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.
}

從源代碼中可以看出以下幾點信息:

  1. 有兩個類成員:_M_ptr(由智能指針接管的普通指針)、_M_refcount(弱引用計數器,類型為__weak_count)
  2. 從構造函數看,_M_ptr獲得了接管的普通指針的值,而_M_refcount的構造并不需要這個值了(這點和__shared_ptr不一樣了),_M_refcount只能借助其他__shared_ptr_M_refcount或者__weak_ptr_M_refcount來進行構造(注意這兩個的_M_refcount類型不同,說明__weak_count支持多種類型進行構造)
  3. 拷貝構造函數和賦值函數的實現同上
  4. 該類依然沒有重載*->運算符,由于接下去已無繼承關系,所以weak_ptr不具備普通指針的特性,無法直接使用資源,這點符合weak_ptr的定義
  5. 既然weak_ptr無法直接使用資源,那他設計_M_ptr這個成員的意圖在哪里呢?答案就是lock方法將weak_ptr轉換為shared_ptr時是需要將這個指針傳遞過去的,不然連接管的指針都沒了轉換的意義也就沒了
  6. 析構函數啥也沒做,因為weak_ptr不持有資源,不對資源的釋放產生影響,接下來對__weak_count進行分析

4.2.3、__weak_count

__weak_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
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;
}

從源代碼可以獲得以下幾點信息:

  1. 有一個類成員:_M_pi(計數器,類型為_Sp_counted_base)
  2. 仔細一看__shared_count里也持有這個成員,類型一模一樣,這樣也就解釋得通為什么__shared_count__weak_count可以互相轉換了,轉換的方式很簡單:
__shared_count轉換為__weak_count的過程為:
拷貝_M_pi,然后調用_M_weak_add_ref方法增加一次弱引用計數__weak_count轉換為__shared_count的過程為:
拷貝_M_pi,然后調用_M_add_ref_copy方法增加一次引用計數
  1. 構造函數、拷貝構造函數、賦值函數均不為_M_pi分配了內存,這點也可以看出weak_ptr確實是shared_ptr的附屬品而已,自己不持有資源不控制資源
  2. 析構函數中調用了_M_pi_M_weak_release方法,釋放了_M_pi的內存(條件滿足的情況下才會釋放)
  3. 接下來的內容和3.1.4小節還有3.1.5小節的內容是一樣的,這里就不贅述

4.2.4、回過頭看weak_ptr中lock方法的實現

weak_ptrlock方法調用了shared_ptr的構造函數如下:

?
1
2
3
4
shared_ptr(const weak_ptr<_Tp>& __r, std::nothrow_t)
    :__shared_ptr<_Tp>(__r, std::nothrow)
{
}

從上面的代碼可以看出調用了__shared_ptr的構造函數,代碼如下:

?
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;
}

可以看到此時先是使用了__weak_ptr_M_refcount成員(類型為__weak_count)來構造__shared_ptr_M_refcount成員(類型為__shared_count),然后再判斷引用計數器是否為0,為零的話就將__shared_ptr_M_ptr成員置為nullptr,即lock函數執行失敗;不為零的話就會正常構建一個shared_ptr了。

上面講的構造_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來構建自己的_M_pi,從前面的分析我們可以知道,在所有的shared_ptrweak_ptr消亡之前,_M_pi的內存是不會被釋放的,所以這里就算之前的shared_ptr已經全部消亡(即資源已釋放),_M_pi還是有效的(因為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; }

代碼比較簡單,意思就是如果此時資源已經被釋放了(對應_M_pi值為nullptr),則會返回0,再回到上面第2點講的那里,_M_ptr將被設置為nullptr,即資源無效,lock函數執行失敗。
至此weak_ptrlock方法的實現原理就全部講解完畢。

4.3、enable_shared_from_this解析

4.3.1、從一個典型的例子來認識智能指針的不足之處

有時候我們需要在一個被shared_ptr管理的對象的內部獲取自己的shared_ptr,比如下面這個的例子:

?
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

從上面這個簡單的例子可以看到,fun輸出的居然是1而不是2,這是為什么?倒回去4.1.2小節可以看到,當使用普通指針(上面的那個this)去構造shared_ptr時,構造出來的shared_ptr一定是獨立的,不與其他人共享的。這樣就會出現一個非常嚴重的問題,那就是析構時會導致對象被重復釋放, 從而引發錯誤

4.3.2、改進方法

現在明確一下我們的需求:在一個對象內部構造該對象的shared_ptr 時,即使該對象已經被shared_ptr管理著,也不會造成對象被兩個獨立的智能指針管理。這就要求我們在對象內構造對象的智能指針時,必須能識別有對象是否已經由其他智能指針管理,智能指針的數量,并且我們創建智能指針后也能讓之前的智能指針感知到。當然標準已經也給出了解決了這個問題辦法,那就是使用接下來所提到的enable_shared_from_this

4.3.3、enable_shared_from_this解析

enable_shared_from_this的實現位于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;
};

從源代碼可以獲得以下幾點信息:

  1. 有一個類成員:_M_weak_this
  2. 該類需要被繼承,被需要用智能指針管理的對象繼承
  3. 我們平時就是使用該類的shared_from_this方法的,可以看到其實現就是利用_M_weak_this構造一個shared_ptr對象而已
  4. 該類并沒有直接初始化_M_weak_this,而是提供了_M_weak_assign方法來構造_M_weak_this,其實現比較簡單,就是調用了weak_ptr_M_assign方法
  5. 那么問題來了,_M_weak_assign方法由誰調用呢?從后面我們可以知道是由一個全局函數__enable_shared_from_this_helper調用的,該函數有一種重載形式是enable_shared_from_this的友元函數,從上面的代碼中就可以看到了,那唯一一個友元函數就是__enable_shared_from_this_helper,里面調用了enable_shared_from_this_M_weak_assign方法。
  6. __enable_shared_from_this_helper函數要在哪個時間點使用才能達到預期的效果呢?答案當然是在__shared_ptr的構造函數中調用。下面列出了__shared_ptr部分構造函數,可以看到確實調用了__enable_shared_from_this_helper,證實了前面的猜想
?
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的實現位于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函數的實現,有兩種形式,第一種就是上面提到過的那個enable_shared_from_this的友元函數,而第二種重載形式里面啥都沒有干。為什么需要重載這兩個函數呢?答案很簡單,當我們一個類繼承了enable_shared_from_this之后,這個類肯定可以轉換為enable_shared_from_this類型了,此時在__shared_ptr中調用的__enable_shared_from_this_helper就是上面第一種情況了,這種情況下就可以使用shared_from_this函數了;反之,當類沒有繼承enable_shared_from_this時,就是調用第二中形式的__enable_shared_from_this_helper,此時也就不能使用shared_from_this函數了。
至此,為什么在使用shared_from_this前,對應的類需要繼承enable_shared_from_this的原因也就全部揭曉了。

5、總結

本文先是簡單介紹了C++智能指針的定義,然后通過對源碼進行詳細分析,我們了解了shared_ptrweak_ptr以及enable_shared_from_this的實現原理。源代碼內容并不是很復雜,沒有用到什么很高深的語法糖,但是閱讀起來非常繞(因為這三個類的關聯錯綜復雜),這就需要我們有耐心地一步一步去深入學習。

最后,如果大家覺得本文寫得好的話麻煩點贊收藏關注一下謝謝,也可以關注該專欄,以后會有更多優質文章輸出的。

原文鏈接:https://blog.csdn.net/weixin_43798887/article/details/116464334

延伸 · 閱讀

精彩推薦
  • C/C++學習C++編程的必備軟件

    學習C++編程的必備軟件

    本文給大家分享的是作者在學習使用C++進行編程的時候所用到的一些常用的軟件,這里推薦給大家...

    謝恩銘10102021-05-08
  • C/C++C語言中炫酷的文件操作實例詳解

    C語言中炫酷的文件操作實例詳解

    內存中的數據都是暫時的,當程序結束時,它們都將丟失,為了永久性的保存大量的數據,C語言提供了對文件的操作,這篇文章主要給大家介紹了關于C語言中文件...

    針眼_6702022-01-24
  • C/C++深入理解goto語句的替代實現方式分析

    深入理解goto語句的替代實現方式分析

    本篇文章是對goto語句的替代實現方式進行了詳細的分析介紹,需要的朋友參考下...

    C語言教程網7342020-12-03
  • C/C++C語言實現電腦關機程序

    C語言實現電腦關機程序

    這篇文章主要為大家詳細介紹了C語言實現電腦關機程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    xiaocaidayong8482021-08-20
  • C/C++C++之重載 重定義與重寫用法詳解

    C++之重載 重定義與重寫用法詳解

    這篇文章主要介紹了C++之重載 重定義與重寫用法詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下...

    青山的青6062022-01-04
  • C/C++c++ 單線程實現同時監聽多個端口

    c++ 單線程實現同時監聽多個端口

    這篇文章主要介紹了c++ 單線程實現同時監聽多個端口的方法,幫助大家更好的理解和學習使用c++,感興趣的朋友可以了解下...

    源之緣11542021-10-27
  • C/C++詳解c語言中的 strcpy和strncpy字符串函數使用

    詳解c語言中的 strcpy和strncpy字符串函數使用

    strcpy 和strcnpy函數是字符串復制函數。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數使用,感興趣的朋友跟隨小編要求看看吧...

    spring-go5642021-07-02
  • C/C++C/C++經典實例之模擬計算器示例代碼

    C/C++經典實例之模擬計算器示例代碼

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關于C/C++經典實例之模擬計算器的相關資料,文中通過示...

    jia150610152021-06-07
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 国产成人精品一区二区三区四区 | av在线免费播 | 91亚洲国产 | 亚洲精品久久 | 久久综合一区 | 久久久久久免费视频 | 国产毛片久久 | 玖玖综合网 | 在线成人亚洲 | 亚洲www视频 | 日韩精品在线视频 | 欧美日韩精品一区 | av成人在线观看 | 中文在线一区二区 | 欧美人成在线视频 | 久久高清 | www.欧美视频 | 粉嫩欧美一区二区三区高清影视 | 久久中文字幕一区二区三区 | 亚洲国产精品一区二区www | 国产精品久久久久久久午夜片 | 亚洲国产精品一区二区久久 | 午夜久久久久 | 日本久久精品 | a免费网站 | 中文字幕日本一区二区 | 亚洲午夜激情 | 午夜欧美一区二区三区在线播放 | 亚洲一区中文 | 国精品一区二区三区 | 中文一区 | 免费观看一级视频 | 免费精品 | 欧美在线a| 午夜免费影视 | 亚洲精品久久久久久国 | 激情综合在线 | 免费亚洲婷婷 | 影音先锋网址 | 国产精品中文字幕在线观看 | 欧美激情视频一区二区三区在线播放 |