0%

智能指针

C++中已经基本不推荐使用裸指针(手动进行newdelete),基本上都是使用智能指针。所以作为C++程序员必须学会智能指针的使用:auto_ptr(已废弃不建议使用)、unique_ptrshare_ptrweak_ptr

画板

auto_ptr(已废弃)

auto_ptr是C++98标准库中提供的智能指针之一,用于管理动态分配的内存资源。它采用独占所有权的方式管理资源,即同一时间只能有一个auto_ptr实例拥有特定资源的所有权。当auto_ptr实例被销毁时,它会自动释放所管理的资源,从而避免了内存泄漏的问题。

1
2
3
auto_ptr<string> sp1(new string("hello world"));
auto_ptr<string> sp2;
sp2 = sp1; // 赋值后sp1为空

auto_ptr被废弃有一下几个问题:

  1. 语义不清。当复制一个auto_ptr对象时(拷贝复制或operator =复制),原对象所持有的堆内存对象也会转移给复制出来的对象。这种是复制语义,但是具体实现却是转移(move),语义上就容易让人混淆,让人逻辑上很容易错误,所以后面出现了unique_ptr代替auto_ptr
  2. 不适用于容器:auto_ptr不能很好地与STL容器一起使用,因为在容器中进行元素的复制或移动操作时会导致指针所有权的转移,可能引发错误。

auto_ptr了解一下就行,主要感觉八股文考的多一些

unique_ptr

unique_ptr是C++11标准引入的智能指针之一,它采用独占所有权的方式,即同一时间只能有一个unique_ptr实例拥有特定资源的所有权。这意味着在任何时刻只有一个unique_ptr可以指向特定的资源,避免了多重所有权可能引发的问题。unique_ptr可以说是auto_ptr的升级版本。

前面auto_ptr代码使用unique_ptr代替:

1
2
3
4
5
std::unique_ptr<std::string> uptr1(new std::string("hello world"));
std::unique_ptr<std::string> uptr2;

// 将 uptr1 的所有权转移给 uptr2,转移后uptr1为nullptr
uptr2 = std::move(uptr1);
  1. auto_ptr 的前车之鉴,unique_ptr 禁止复制语义,为了达到这个效果,std::unique_ptr 类的拷贝构造函数和赋值运算符(operator =)被标记为 deleteunique_ptr 只能通过移动构造或者移动赋值进行转移
  2. 自定义删除器。unique_ptr默认的删除器是delete,但是有时候我们想定义自己的删除器,这个也是可以实现的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <memory>
#include <iostream>

int main() {
// 使用auto和lambda表达式定义自定义删除器
auto customDeleter = [](int* ptr) {
std::cout << "自定义删除器被调用,释放资源:" << *ptr << std::endl;
delete ptr;
};

// 使用自定义删除器
std::unique_ptr<int, decltype(customDeleter)> uptr(new int(42), customDeleter);

// 手动释放资源
uptr.reset();

return 0;
}
  1. unique_ptr的指针大小和普通指针相等,所以一般没有特殊要求,我们可以使用unique_ptr最节约内存。

源码分析

unique_ptr其中的源码(visual studio C++20):

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
template <class _Ty, class _Dx /* = default_delete<_Ty> */>
class unique_ptr { // non-copyable pointer to an object
public:
// 指针类型
using pointer = typename _Get_deleter_pointer_type<_Ty, remove_reference_t<_Dx>>::type;
// 元素类型
using element_type = _Ty;
// 删除器类型
using deleter_type = _Dx;

template <class _Dx2 = _Dx, _Unique_ptr_enable_default_t<_Dx2> = 0>
constexpr unique_ptr() noexcept : _Mypair(_Zero_then_variadic_args_t{}) {}

template <class _Dx2 = _Dx, _Unique_ptr_enable_default_t<_Dx2> = 0>
constexpr unique_ptr(nullptr_t) noexcept : _Mypair(_Zero_then_variadic_args_t{}) {}

_CONSTEXPR23 unique_ptr& operator=(nullptr_t) noexcept {
reset();
return *this;
}

// The Standard depicts these constructors that accept pointer as taking type_identity_t<pointer> to inhibit CTAD.
// Since pointer is an opaque type alias in our implementation, it inhibits CTAD without extra decoration.
template <class _Dx2 = _Dx, _Unique_ptr_enable_default_t<_Dx2> = 0>
_CONSTEXPR23 explicit unique_ptr(pointer _Ptr) noexcept : _Mypair(_Zero_then_variadic_args_t{}, _Ptr) {}

template <class _Dx2 = _Dx, enable_if_t<is_constructible_v<_Dx2, const _Dx2&>, int> = 0>
_CONSTEXPR23 unique_ptr(pointer _Ptr, const _Dx& _Dt) noexcept : _Mypair(_One_then_variadic_args_t{}, _Dt, _Ptr) {}

template <class _Dx2 = _Dx,
enable_if_t<conjunction_v<negation<is_reference<_Dx2>>, is_constructible<_Dx2, _Dx2>>, int> = 0>
_CONSTEXPR23 unique_ptr(pointer _Ptr, _Dx&& _Dt) noexcept
: _Mypair(_One_then_variadic_args_t{}, _STD move(_Dt), _Ptr) {}

template <class _Dx2 = _Dx,
enable_if_t<conjunction_v<is_reference<_Dx2>, is_constructible<_Dx2, remove_reference_t<_Dx2>>>, int> = 0>
unique_ptr(pointer, remove_reference_t<_Dx>&&) = delete;
// 移动构造
template <class _Dx2 = _Dx, enable_if_t<is_move_constructible_v<_Dx2>, int> = 0>
_CONSTEXPR23 unique_ptr(unique_ptr&& _Right) noexcept
: _Mypair(_One_then_variadic_args_t{}, _STD forward<_Dx>(_Right.get_deleter()), _Right.release()) {}

template <class _Ty2, class _Dx2,
enable_if_t<
conjunction_v<negation<is_array<_Ty2>>, is_convertible<typename unique_ptr<_Ty2, _Dx2>::pointer, pointer>,
conditional_t<is_reference_v<_Dx>, is_same<_Dx2, _Dx>, is_convertible<_Dx2, _Dx>>>,
int> = 0>
_CONSTEXPR23 unique_ptr(unique_ptr<_Ty2, _Dx2>&& _Right) noexcept
: _Mypair(_One_then_variadic_args_t{}, _STD forward<_Dx2>(_Right.get_deleter()), _Right.release()) {}

template <class _Ty2, class _Dx2,
enable_if_t<conjunction_v<negation<is_array<_Ty2>>, is_assignable<_Dx&, _Dx2>,
is_convertible<typename unique_ptr<_Ty2, _Dx2>::pointer, pointer>>,
int> = 0>
_CONSTEXPR23 unique_ptr& operator=(unique_ptr<_Ty2, _Dx2>&& _Right) noexcept {
reset(_Right.release());
_Mypair._Get_first() = _STD forward<_Dx2>(_Right._Mypair._Get_first());
return *this;
}
// 移动赋值
template <class _Dx2 = _Dx, enable_if_t<is_move_assignable_v<_Dx2>, int> = 0>
_CONSTEXPR23 unique_ptr& operator=(unique_ptr&& _Right) noexcept {
if (this != _STD addressof(_Right)) {
reset(_Right.release());
_Mypair._Get_first() = _STD forward<_Dx>(_Right._Mypair._Get_first());
}
return *this;
}

_CONSTEXPR23 void swap(unique_ptr& _Right) noexcept {
_Swap_adl(_Mypair._Myval2, _Right._Mypair._Myval2);
_Swap_adl(_Mypair._Get_first(), _Right._Mypair._Get_first());
}

_CONSTEXPR23 ~unique_ptr() noexcept {
if (_Mypair._Myval2) {
// 调用 first删除器 删除 指针
_Mypair._Get_first()(_Mypair._Myval2);
}
}

_NODISCARD _CONSTEXPR23 _Dx& get_deleter() noexcept {
return _Mypair._Get_first();
}

_NODISCARD _CONSTEXPR23 const _Dx& get_deleter() const noexcept {
return _Mypair._Get_first();
}

_NODISCARD _CONSTEXPR23 add_lvalue_reference_t<_Ty> operator*() const noexcept(noexcept(*_STD declval<pointer>())) {
return *_Mypair._Myval2;
}

_NODISCARD _CONSTEXPR23 pointer operator->() const noexcept {
return _Mypair._Myval2;
}

_NODISCARD _CONSTEXPR23 pointer get() const noexcept {
return _Mypair._Myval2;
}

_CONSTEXPR23 explicit operator bool() const noexcept {
return static_cast<bool>(_Mypair._Myval2);
}

_CONSTEXPR23 pointer release() noexcept {
return _STD exchange(_Mypair._Myval2, nullptr);
}

_CONSTEXPR23 void reset(pointer _Ptr = nullptr) noexcept {
pointer _Old = _STD exchange(_Mypair._Myval2, _Ptr);
if (_Old) {
_Mypair._Get_first()(_Old);
}
}
// 复制操作设置为delete
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;

private:
template <class, class>
friend class unique_ptr;
// _Mypair first是删除器,second是指针
_Compressed_pair<_Dx, pointer> _Mypair;
};

unique_ptr实现粗略看比较简单,数据成员比较重要的就是_Mypair,里面存放者删除器和指针,在析构的时候就调用删除器删除指针。

share_ptr

share_ptr通过引用计数的方式来控制资源的释放,允许多个指针指向同一个资源(共享),每多一个std::shared_ptr对资源的引用,资源引用计数将增加1,每一个指向该资源的std::shared_ptr对象析构时,资源引用计数减1,最后一个std::shared_ptr对象析构时,发现资源计数为0,将释放其持有的资源。

1
2
3
4
5
6
7
8
9
10
//初始化方式1
std::shared_ptr<int> sp1(new int(123));

//初始化方式2
std::shared_ptr<int> sp2;
sp2.reset(new int(123));

//初始化方式3
std::shared_ptr<int> sp3;
sp3 = std::make_shared<int>(123);
  1. 自定义删除器,shared_ptr自定义删除器比unique_ptr 简单一些,不会影响指针指针的类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <memory>
#include <iostream>

int main() {
// 使用auto和lambda表达式定义自定义删除器
auto customDeleter = [](int* ptr) {
std::cout << "自定义删除器被调用,释放资源:" << *ptr << std::endl;
delete ptr;
};

// 使用auto和lambda表达式实现自定义删除器的shared_ptr
auto sptr = std::shared_ptr<int>(new int(42), customDeleter);

// 使用shared_ptr访问资源
std::cout << "sptr指向的整数是: " << *sptr << std::endl;

// 当最后一个shared_ptr销毁时,自定义删除器会被调用
return 0;
}
  1. shared_ptr有两个指针,一个指针指向资源,一个指针指向控制块,所以shared_ptr大小其实是普通指针的两倍,并且每次shared_ptr操作都会对控制块进行操作(对引用技术进行操作),所以效率没有unique_ptr高。

源码分析

先看类图

  1. shared_ptr 继承_Ptr_base,关于指针和控制块的部分都是在_Ptr_base中,shared_ptr调用_Ptr_base中内容进行引用计算的增加和减少
  2. _Ptr_base重要的数据成员:_Ptr_Rep两个指针一个指向资源、一个指向控制块(_Ref_count_base
  3. _Ref_count_base(控制块)是个纯虚类,里面有uses和weaks两个引用控制块
  4. _Ref_count_base有三个实现类_Ref_count_Ref_count_resource_Ref_count_resource_alloc,分别代表普通引用计数、带删除器的引用计数、带删除器和分配器的引用计数
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
template <class _Ty>
class shared_ptr : public _Ptr_base<_Ty> { // class for reference counted resource management
private:
using _Mybase = _Ptr_base<_Ty>;
public:
using typename _Mybase::element_type;

#if _HAS_CXX17
using weak_type = weak_ptr<_Ty>;
#endif // _HAS_CXX17

constexpr shared_ptr() noexcept = default;

constexpr shared_ptr(nullptr_t) noexcept {} // construct empty shared_ptr

template <class _Ux,
enable_if_t<conjunction_v<conditional_t<is_array_v<_Ty>, _Can_array_delete<_Ux>, _Can_scalar_delete<_Ux>>,
_SP_convertible<_Ux, _Ty>>,
int> = 0>
explicit shared_ptr(_Ux* _Px) { // construct shared_ptr object that owns _Px
if constexpr (is_array_v<_Ty>) {
_Setpd(_Px, default_delete<_Ux[]>{});
} else {
_Temporary_owner<_Ux> _Owner(_Px);
// 此函数和enable_share_from_this相关
_Set_ptr_rep_and_enable_shared(_Owner._Ptr, new _Ref_count<_Ux>(_Owner._Ptr));
_Owner._Ptr = nullptr;
}
}

template <class _Ux, class _Dx,
enable_if_t<conjunction_v<is_move_constructible<_Dx>, _Can_call_function_object<_Dx&, _Ux*&>,
_SP_convertible<_Ux, _Ty>>,
int> = 0>
shared_ptr(_Ux* _Px, _Dx _Dt) { // construct with _Px, deleter
_Setpd(_Px, _STD move(_Dt));
}

template <class _Ux, class _Dx, class _Alloc,
enable_if_t<conjunction_v<is_move_constructible<_Dx>, _Can_call_function_object<_Dx&, _Ux*&>,
_SP_convertible<_Ux, _Ty>>,
int> = 0>
shared_ptr(_Ux* _Px, _Dx _Dt, _Alloc _Ax) { // construct with _Px, deleter, allocator
_Setpda(_Px, _STD move(_Dt), _Ax);
}

template <class _Dx,
enable_if_t<conjunction_v<is_move_constructible<_Dx>, _Can_call_function_object<_Dx&, nullptr_t&>>, int> = 0>
shared_ptr(nullptr_t, _Dx _Dt) { // construct with nullptr, deleter
_Setpd(nullptr, _STD move(_Dt));
}

template <class _Dx, class _Alloc,
enable_if_t<conjunction_v<is_move_constructible<_Dx>, _Can_call_function_object<_Dx&, nullptr_t&>>, int> = 0>
shared_ptr(nullptr_t, _Dx _Dt, _Alloc _Ax) { // construct with nullptr, deleter, allocator
_Setpda(nullptr, _STD move(_Dt), _Ax);
}

template <class _Ty2>
shared_ptr(const shared_ptr<_Ty2>& _Right, element_type* _Px) noexcept {
// construct shared_ptr object that aliases _Right
this->_Alias_construct_from(_Right, _Px);
}

template <class _Ty2>
shared_ptr(shared_ptr<_Ty2>&& _Right, element_type* _Px) noexcept {
// move construct shared_ptr object that aliases _Right
this->_Alias_move_construct_from(_STD move(_Right), _Px);
}

shared_ptr(const shared_ptr& _Other) noexcept { // construct shared_ptr object that owns same resource as _Other
this->_Copy_construct_from(_Other);
}

template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
shared_ptr(const shared_ptr<_Ty2>& _Other) noexcept {
// construct shared_ptr object that owns same resource as _Other
this->_Copy_construct_from(_Other);
}

shared_ptr(shared_ptr&& _Right) noexcept { // construct shared_ptr object that takes resource from _Right
this->_Move_construct_from(_STD move(_Right));
}

template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
shared_ptr(shared_ptr<_Ty2>&& _Right) noexcept { // construct shared_ptr object that takes resource from _Right
this->_Move_construct_from(_STD move(_Right));
}

template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
explicit shared_ptr(const weak_ptr<_Ty2>& _Other) { // construct shared_ptr object that owns resource *_Other
if (!this->_Construct_from_weak(_Other)) {
_Throw_bad_weak_ptr();
}
}

template <class _Ux, class _Dx,
enable_if_t<conjunction_v<_SP_pointer_compatible<_Ux, _Ty>,
is_convertible<typename unique_ptr<_Ux, _Dx>::pointer, element_type*>>,
int> = 0>
// 从unique_ptr进行构造
shared_ptr(unique_ptr<_Ux, _Dx>&& _Other) {
using _Fancy_t = typename unique_ptr<_Ux, _Dx>::pointer;
using _Raw_t = typename unique_ptr<_Ux, _Dx>::element_type*;
using _Deleter_t = conditional_t<is_reference_v<_Dx>, decltype(_STD ref(_Other.get_deleter())), _Dx>;

const _Fancy_t _Fancy = _Other.get();

if (_Fancy) {
const _Raw_t _Raw = _Fancy;
const auto _Rx =
new _Ref_count_resource<_Fancy_t, _Deleter_t>(_Fancy, _STD forward<_Dx>(_Other.get_deleter()));
_Set_ptr_rep_and_enable_shared(_Raw, _Rx);
_Other.release();
}
}
// 析构的时候调用_Decref减少引用
~shared_ptr() noexcept { // release resource
this->_Decref();
}

shared_ptr& operator=(const shared_ptr& _Right) noexcept {
shared_ptr(_Right).swap(*this);
return *this;
}

template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
shared_ptr& operator=(const shared_ptr<_Ty2>& _Right) noexcept {
shared_ptr(_Right).swap(*this);
return *this;
}

shared_ptr& operator=(shared_ptr&& _Right) noexcept { // take resource from _Right
shared_ptr(_STD move(_Right)).swap(*this);
return *this;
}

template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
shared_ptr& operator=(shared_ptr<_Ty2>&& _Right) noexcept { // take resource from _Right
shared_ptr(_STD move(_Right)).swap(*this);
return *this;
}

template <class _Ux, class _Dx,
enable_if_t<conjunction_v<_SP_pointer_compatible<_Ux, _Ty>,
is_convertible<typename unique_ptr<_Ux, _Dx>::pointer, element_type*>>,
int> = 0>
shared_ptr& operator=(unique_ptr<_Ux, _Dx>&& _Right) { // move from unique_ptr
shared_ptr(_STD move(_Right)).swap(*this);
return *this;
}

void swap(shared_ptr& _Other) noexcept {
this->_Swap(_Other);
}

void reset() noexcept { // release resource and convert to empty shared_ptr object
shared_ptr().swap(*this);
}

template <class _Ux,
enable_if_t<conjunction_v<conditional_t<is_array_v<_Ty>, _Can_array_delete<_Ux>, _Can_scalar_delete<_Ux>>,
_SP_convertible<_Ux, _Ty>>,
int> = 0>
void reset(_Ux* _Px) { // release, take ownership of _Px
shared_ptr(_Px).swap(*this);
}

template <class _Ux, class _Dx,
enable_if_t<conjunction_v<is_move_constructible<_Dx>, _Can_call_function_object<_Dx&, _Ux*&>,
_SP_convertible<_Ux, _Ty>>,
int> = 0>
void reset(_Ux* _Px, _Dx _Dt) { // release, take ownership of _Px, with deleter _Dt
shared_ptr(_Px, _Dt).swap(*this);
}

template <class _Ux, class _Dx, class _Alloc,
enable_if_t<conjunction_v<is_move_constructible<_Dx>, _Can_call_function_object<_Dx&, _Ux*&>,
_SP_convertible<_Ux, _Ty>>,
int> = 0>
void reset(_Ux* _Px, _Dx _Dt, _Alloc _Ax) { // release, take ownership of _Px, with deleter _Dt, allocator _Ax
shared_ptr(_Px, _Dt, _Ax).swap(*this);
}

using _Mybase::get;

template <class _Ty2 = _Ty, enable_if_t<!disjunction_v<is_array<_Ty2>, is_void<_Ty2>>, int> = 0>
_NODISCARD _Ty2& operator*() const noexcept {
return *get();
}

template <class _Ty2 = _Ty, enable_if_t<!is_array_v<_Ty2>, int> = 0>
_NODISCARD _Ty2* operator->() const noexcept {
return get();
}

template <class _Ty2 = _Ty, class _Elem = element_type, enable_if_t<is_array_v<_Ty2>, int> = 0>
_NODISCARD _Elem& operator[](ptrdiff_t _Idx) const noexcept /* strengthened */ {
return get()[_Idx];
}

explicit operator bool() const noexcept {
return get() != nullptr;
}

private:
template <class _UxptrOrNullptr, class _Dx>
void _Setpd(const _UxptrOrNullptr _Px, _Dx _Dt) { // take ownership of _Px, deleter _Dt
_Temporary_owner_del<_UxptrOrNullptr, _Dx> _Owner(_Px, _Dt);
_Set_ptr_rep_and_enable_shared(
_Owner._Ptr, new _Ref_count_resource<_UxptrOrNullptr, _Dx>(_Owner._Ptr, _STD move(_Dt)));
_Owner._Call_deleter = false;
}

template <class _UxptrOrNullptr, class _Dx, class _Alloc>
void _Setpda(const _UxptrOrNullptr _Px, _Dx _Dt, _Alloc _Ax) { // take ownership of _Px, deleter _Dt, allocator _Ax
using _Alref_alloc = _Rebind_alloc_t<_Alloc, _Ref_count_resource_alloc<_UxptrOrNullptr, _Dx, _Alloc>>;

_Temporary_owner_del<_UxptrOrNullptr, _Dx> _Owner(_Px, _Dt);
_Alref_alloc _Alref(_Ax);
_Alloc_construct_ptr<_Alref_alloc> _Constructor(_Alref);
_Constructor._Allocate();
_Construct_in_place(*_Constructor._Ptr, _Owner._Ptr, _STD move(_Dt), _Ax);
_Set_ptr_rep_and_enable_shared(_Owner._Ptr, _Unfancy(_Constructor._Ptr));
_Constructor._Ptr = nullptr;
_Owner._Call_deleter = false;
}

template <class _Ux>
void _Set_ptr_rep_and_enable_shared(_Ux* const _Px, _Ref_count_base* const _Rx) noexcept { // take ownership of _Px
this->_Ptr = _Px;
this->_Rep = _Rx;
if constexpr (conjunction_v<negation<is_array<_Ty>>, negation<is_volatile<_Ux>>, _Can_enable_shared<_Ux>>) {
if (_Px && _Px->_Wptr.expired()) {
_Px->_Wptr = shared_ptr<remove_cv_t<_Ux>>(*this, const_cast<remove_cv_t<_Ux>*>(_Px));
}
}
}

void _Set_ptr_rep_and_enable_shared(nullptr_t, _Ref_count_base* const _Rx) noexcept { // take ownership of nullptr
this->_Ptr = nullptr;
this->_Rep = _Rx;
}
};
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
149
150
151
152
153
154
155
156
157
158
159
template <class _Ty>
class _Ptr_base { // base class for shared_ptr and weak_ptr
public:
using element_type = remove_extent_t<_Ty>;

_NODISCARD long use_count() const noexcept {
return _Rep ? _Rep->_Use_count() : 0;
}

template <class _Ty2>
_NODISCARD bool owner_before(const _Ptr_base<_Ty2>& _Right) const noexcept { // compare addresses of manager objects
return _Rep < _Right._Rep;
}

_Ptr_base(const _Ptr_base&) = delete;
_Ptr_base& operator=(const _Ptr_base&) = delete;

protected:
_NODISCARD element_type* get() const noexcept {
return _Ptr;
}

constexpr _Ptr_base() noexcept = default;

~_Ptr_base() = default;

template <class _Ty2>
void _Move_construct_from(_Ptr_base<_Ty2>&& _Right) noexcept {
// implement shared_ptr's (converting) move ctor and weak_ptr's move ctor
_Ptr = _Right._Ptr;
_Rep = _Right._Rep;

_Right._Ptr = nullptr;
_Right._Rep = nullptr;
}
// 增加引用计数
template <class _Ty2>
void _Copy_construct_from(const shared_ptr<_Ty2>& _Other) noexcept {
// implement shared_ptr's (converting) copy ctor
_Other._Incref();

_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
}

template <class _Ty2>
void _Alias_construct_from(const shared_ptr<_Ty2>& _Other, element_type* _Px) noexcept {
// implement shared_ptr's aliasing ctor
_Other._Incref();

_Ptr = _Px;
_Rep = _Other._Rep;
}

template <class _Ty2>
void _Alias_move_construct_from(shared_ptr<_Ty2>&& _Other, element_type* _Px) noexcept {
// implement shared_ptr's aliasing move ctor
_Ptr = _Px;
_Rep = _Other._Rep;

_Other._Ptr = nullptr;
_Other._Rep = nullptr;
}

template <class _Ty0>
friend class weak_ptr; // specifically, weak_ptr::lock()

template <class _Ty2>
bool _Construct_from_weak(const weak_ptr<_Ty2>& _Other) noexcept {
// implement shared_ptr's ctor from weak_ptr, and weak_ptr::lock()
if (_Other._Rep && _Other._Rep->_Incref_nz()) {
_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
return true;
}

return false;
}
// User增加引用计数
void _Incref() const noexcept {
if (_Rep) {
_Rep->_Incref();
}
}
// User减少引用计数
void _Decref() noexcept { // decrement reference count
if (_Rep) {
_Rep->_Decref();
}
}

void _Swap(_Ptr_base& _Right) noexcept { // swap pointers
_STD swap(_Ptr, _Right._Ptr);
_STD swap(_Rep, _Right._Rep);
}

template <class _Ty2>
void _Weakly_construct_from(const _Ptr_base<_Ty2>& _Other) noexcept { // implement weak_ptr's ctors
if (_Other._Rep) {
_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
_Rep->_Incwref();
} else {
_STL_INTERNAL_CHECK(!_Ptr && !_Rep);
}
}

template <class _Ty2>
void _Weakly_convert_lvalue_avoiding_expired_conversions(const _Ptr_base<_Ty2>& _Other) noexcept {
// implement weak_ptr's copy converting ctor
if (_Other._Rep) {
_Rep = _Other._Rep; // always share ownership
_Rep->_Incwref();

if (_Rep->_Incref_nz()) {
_Ptr = _Other._Ptr; // keep resource alive during conversion, handling virtual inheritance
_Rep->_Decref();
} else {
_STL_INTERNAL_CHECK(!_Ptr);
}
} else {
_STL_INTERNAL_CHECK(!_Ptr && !_Rep);
}
}

template <class _Ty2>
void _Weakly_convert_rvalue_avoiding_expired_conversions(_Ptr_base<_Ty2>&& _Other) noexcept {
// implement weak_ptr's move converting ctor
_Rep = _Other._Rep; // always transfer ownership
_Other._Rep = nullptr;

if (_Rep && _Rep->_Incref_nz()) {
_Ptr = _Other._Ptr; // keep resource alive during conversion, handling virtual inheritance
_Rep->_Decref();
} else {
_STL_INTERNAL_CHECK(!_Ptr);
}

_Other._Ptr = nullptr;
}
// weaks增加引用计数
void _Incwref() const noexcept {
if (_Rep) {
_Rep->_Incwref();
}
}
// weaks减少引用计数
void _Decwref() noexcept { // decrement weak reference count
if (_Rep) {
_Rep->_Decwref();
}
}

private:
// 智能指针关联的原始指针
element_type* _Ptr{nullptr};
// 管理智能指针的 控制块
_Ref_count_base* _Rep{nullptr};
};
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
class __declspec(novtable) _Ref_count_base { // common code for reference counting
private:
#ifdef _M_CEE_PURE
// permanent workaround to avoid mentioning _purecall in msvcurt.lib, ptrustu.lib, or other support libs
virtual void _Destroy() noexcept {
_STD terminate();
}

virtual void _Delete_this() noexcept {
_STD terminate();
}
#else // ^^^ _M_CEE_PURE / !_M_CEE_PURE vvv
// 纯虚函数
virtual void _Destroy() noexcept = 0; // destroy managed resource
virtual void _Delete_this() noexcept = 0; // destroy self
#endif // _M_CEE_PURE
// 强指针的引用计数(原子)
_Atomic_counter_t _Uses = 1;
// 弱指针的引用计数(原子)
_Atomic_counter_t _Weaks = 1;

protected:
constexpr _Ref_count_base() noexcept = default; // non-atomic initializations

public:
// 不允许复制
_Ref_count_base(const _Ref_count_base&) = delete;
_Ref_count_base& operator=(const _Ref_count_base&) = delete;

virtual ~_Ref_count_base() noexcept {} // TRANSITION, should be non-virtual

bool _Incref_nz() noexcept { // increment use count if not zero, return true if successful
auto& _Volatile_uses = reinterpret_cast<volatile long&>(_Uses);
#ifdef _M_CEE_PURE
long _Count = *_Atomic_address_as<const long>(&_Volatile_uses);
#else
long _Count = __iso_volatile_load32(reinterpret_cast<volatile int*>(&_Volatile_uses));
#endif
while (_Count != 0) {
const long _Old_value = _INTRIN_RELAXED(_InterlockedCompareExchange)(&_Volatile_uses, _Count + 1, _Count);
if (_Old_value == _Count) {
return true;
}

_Count = _Old_value;
}

return false;
}

void _Incref() noexcept { // increment use count
_MT_INCR(_Uses);
}

void _Incwref() noexcept { // increment weak reference count
_MT_INCR(_Weaks);
}

void _Decref() noexcept { // decrement use count
if (_MT_DECR(_Uses) == 0) {
_Destroy();
_Decwref();
}
}

void _Decwref() noexcept { // decrement weak reference count
if (_MT_DECR(_Weaks) == 0) {
_Delete_this();
}
}

long _Use_count() const noexcept {
return static_cast<long>(_Uses);
}

virtual void* _Get_deleter(const type_info&) const noexcept {
return nullptr;
}
};
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
// 不带删除器的引用计数
template <class _Ty>
class _Ref_count : public _Ref_count_base { // handle reference counting for pointer without deleter
public:
explicit _Ref_count(_Ty* _Px) : _Ref_count_base(), _Ptr(_Px) {}

private:
void _Destroy() noexcept override { // destroy managed resource
delete _Ptr;
}

void _Delete_this() noexcept override { // destroy self
delete this;
}

_Ty* _Ptr;
};
// 带删除器的引用计数
template <class _Resource, class _Dx>
class _Ref_count_resource : public _Ref_count_base { // handle reference counting for object with deleter
public:
_Ref_count_resource(_Resource _Px, _Dx _Dt)
: _Ref_count_base(), _Mypair(_One_then_variadic_args_t{}, _STD move(_Dt), _Px) {}

#ifdef __EDG__ // TRANSITION, VSO-1292293
~_Ref_count_resource() noexcept override {} // TRANSITION, should be non-virtual
#else // ^^^ workaround / no workaround vvv
~_Ref_count_resource() noexcept override = default; // TRANSITION, should be non-virtual
#endif // ^^^ no workaround ^^^

void* _Get_deleter(const type_info& _Typeid) const noexcept override {
#if _HAS_STATIC_RTTI
if (_Typeid == typeid(_Dx)) {
return const_cast<_Dx*>(_STD addressof(_Mypair._Get_first()));
}
#else // _HAS_STATIC_RTTI
(void) _Typeid;
#endif // _HAS_STATIC_RTTI

return nullptr;
}

private:
void _Destroy() noexcept override { // destroy managed resource
_Mypair._Get_first()(_Mypair._Myval2);
}

void _Delete_this() noexcept override { // destroy self
delete this;
}

_Compressed_pair<_Dx, _Resource> _Mypair;
};

// 带删除器和分配器 的 引用计数
template <class _Resource, class _Dx, class _Alloc>
class _Ref_count_resource_alloc : public _Ref_count_base {
// handle reference counting for object with deleter and allocator
public:
_Ref_count_resource_alloc(_Resource _Px, _Dx _Dt, const _Alloc& _Ax)
: _Ref_count_base(),
_Mypair(_One_then_variadic_args_t{}, _STD move(_Dt), _One_then_variadic_args_t{}, _Ax, _Px) {}

#ifdef __EDG__ // TRANSITION, VSO-1292293
~_Ref_count_resource_alloc() noexcept override {} // TRANSITION, should be non-virtual
#else // ^^^ workaround / no workaround vvv
~_Ref_count_resource_alloc() noexcept override = default; // TRANSITION, should be non-virtual
#endif // ^^^ no workaround ^^^

void* _Get_deleter(const type_info& _Typeid) const noexcept override {
#if _HAS_STATIC_RTTI
if (_Typeid == typeid(_Dx)) {
return const_cast<_Dx*>(_STD addressof(_Mypair._Get_first()));
}
#else // _HAS_STATIC_RTTI
(void) _Typeid;
#endif // _HAS_STATIC_RTTI

return nullptr;
}

private:
using _Myalty = _Rebind_alloc_t<_Alloc, _Ref_count_resource_alloc>;

void _Destroy() noexcept override { // destroy managed resource
_Mypair._Get_first()(_Mypair._Myval2._Myval2);
}

void _Delete_this() noexcept override { // destroy self
_Myalty _Al = _Mypair._Myval2._Get_first();
this->~_Ref_count_resource_alloc();
_Deallocate_plain(_Al, this);
}

_Compressed_pair<_Dx, _Compressed_pair<_Myalty, _Resource>> _Mypair;
};

weak_ptr

std::weak_ptr 是 C++ 标准库中的智能指针,用于解决 std::shared_ptr 的循环引用(circular reference)问题。std::weak_ptr 是一种弱引用,不会增加引用计数,也不会拥有被管理的对象。它通常用于解决由 std::shared_ptr 形成的循环引用导致的内存泄漏问题,简单来说weak_ptr就是用来配合share_ptr一起进行使用

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
#include <iostream>
#include <memory>

class Person {
public:
std::string name;
std::weak_ptr<Person> partner;

Person(const std::string& n) : name(n) {
std::cout << name << " is created\n";
}

~Person() {
std::cout << name << " is destroyed\n";
}
};

int main() {
std::shared_ptr<Person> alice = std::make_shared<Person>("Alice");
std::shared_ptr<Person> bob = std::make_shared<Person>("Bob");

alice->partner = bob;
bob->partner = alice;

// 使用 weak_ptr 创建 shared_ptr,避免循环引用
std::shared_ptr<Person> alicePartner = alice->partner.lock();
std::shared_ptr<Person> bobPartner = bob->partner.lock();

if (alicePartner && bobPartner) {
std::cout << alice->name << "'s partner is: " << alicePartner->name << std::endl;
std::cout << bob->name << "'s partner is: " << bobPartner->name << std::endl;
} else {
std::cout << "One of the partners is no longer available\n";
}

return 0;
}

通过前面share_ptr源码的理解,我们也能猜测weak_ptr是通过控制块中weaks进行控制的,从源码角度来说weak_ptr代码里面应该是对weaks进行控制,当有一个新的weak_ptr指向share_ptr式,weaks数量进行增加,

所以内存释放来说:

  • Uses 为0时,share_ptr指向资源会进行释放
  • weaks为0时,控制块内存才最终进行了释放
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
template <class _Ty>
class weak_ptr : public _Ptr_base<_Ty> { // class for pointer to reference counted resource
public:
#ifndef _M_CEE_PURE
// When a constructor is converting from weak_ptr<_Ty2> to weak_ptr<_Ty>, the below type trait intentionally asks
// whether it would be possible to static_cast from _Ty* to const _Ty2*; see N4901 [expr.static.cast]/11.

// Primary template, the value is used when the substitution fails.
template <class _Ty2, class = const _Ty2*>
static constexpr bool _Must_avoid_expired_conversions_from = true;

// Template specialization, the value is used when the substitution succeeds.
template <class _Ty2>
static constexpr bool
_Must_avoid_expired_conversions_from<_Ty2, decltype(static_cast<const _Ty2*>(static_cast<_Ty*>(nullptr)))> =
false;
#endif // _M_CEE_PURE

constexpr weak_ptr() noexcept {}

weak_ptr(const weak_ptr& _Other) noexcept {
this->_Weakly_construct_from(_Other); // same type, no conversion
}

template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
weak_ptr(const shared_ptr<_Ty2>& _Other) noexcept {
this->_Weakly_construct_from(_Other); // shared_ptr keeps resource alive during conversion
}

template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
weak_ptr(const weak_ptr<_Ty2>& _Other) noexcept {
#ifdef _M_CEE_PURE
constexpr bool _Avoid_expired_conversions = true; // slow, but always safe; avoids error LNK1179
#else // _M_CEE_PURE
constexpr bool _Avoid_expired_conversions = _Must_avoid_expired_conversions_from<_Ty2>;
#endif // _M_CEE_PURE

if constexpr (_Avoid_expired_conversions) {
this->_Weakly_convert_lvalue_avoiding_expired_conversions(_Other);
} else {
this->_Weakly_construct_from(_Other);
}
}

weak_ptr(weak_ptr&& _Other) noexcept {
this->_Move_construct_from(_STD move(_Other));
}

template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
weak_ptr(weak_ptr<_Ty2>&& _Other) noexcept {
#ifdef _M_CEE_PURE
constexpr bool _Avoid_expired_conversions = true; // slow, but always safe; avoids error LNK1179
#else // _M_CEE_PURE
constexpr bool _Avoid_expired_conversions = _Must_avoid_expired_conversions_from<_Ty2>;
#endif // _M_CEE_PURE

if constexpr (_Avoid_expired_conversions) {
this->_Weakly_convert_rvalue_avoiding_expired_conversions(_STD move(_Other));
} else {
this->_Move_construct_from(_STD move(_Other));
}
}
// 减少weaks引用计数
~weak_ptr() noexcept {
this->_Decwref();
}

weak_ptr& operator=(const weak_ptr& _Right) noexcept {
weak_ptr(_Right).swap(*this);
return *this;
}

template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
weak_ptr& operator=(const weak_ptr<_Ty2>& _Right) noexcept {
weak_ptr(_Right).swap(*this);
return *this;
}

weak_ptr& operator=(weak_ptr&& _Right) noexcept {
weak_ptr(_STD move(_Right)).swap(*this);
return *this;
}

template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
weak_ptr& operator=(weak_ptr<_Ty2>&& _Right) noexcept {
weak_ptr(_STD move(_Right)).swap(*this);
return *this;
}

template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
weak_ptr& operator=(const shared_ptr<_Ty2>& _Right) noexcept {
weak_ptr(_Right).swap(*this);
return *this;
}

void reset() noexcept { // release resource, convert to null weak_ptr object
weak_ptr{}.swap(*this);
}

void swap(weak_ptr& _Other) noexcept {
this->_Swap(_Other);
}

_NODISCARD bool expired() const noexcept {
return this->use_count() == 0;
}

_NODISCARD shared_ptr<_Ty> lock() const noexcept { // convert to shared_ptr
shared_ptr<_Ty> _Ret;
(void) _Ret._Construct_from_weak(*this);
return _Ret;
}
};

make_uniquemake_shared

现代C++建议多使用make_uniquemake_shared,原因主要是两点

  • 性能优势std::make_shared可以在单个内存分配中同时分配控制块和对象内存,而直接使用std::shared_ptr则需要分别进行两次内存分配。这样可以提高内存访问的效率并减少内存碎片。(仅仅针对make_shared
  • 异常安全性std::make_shared可以确保在内存分配失败时不会泄漏内存,因为它是原子操作,要么成功创建std::shared_ptr,要么不创建,不会出现中间状态。

第二点异常安全性的解释:

假设我们有个函数按照某种优先级处理Widget:

1
void processWidget(std::shared_ptr<Widget> spw, int priority);

现在假设我们有一个函数来计算相关的优先级,
1
int computePriority();

并且我们在调用processWidget时使用了new而不是std::make_shared
1
2
processWidget(std::shared_ptr<Widget>(new Widget),  //潜在的资源泄漏!
computePriority());

在运行时,一个函数的实参必须先被计算,这个函数再被调用,所以在调用processWidget之前,必须执行以下操作,processWidget才开始执行:

  • 表达式new Widget必须计算,例如,一个Widget对象必须在堆上被创建
  • 负责管理new出来指针的std::shared_ptr<Widget>构造函数必须被执行
  • computePriority必须运行

编译器不需要按照执行顺序生成代码。new Widget必须在std::shared_ptr的构造函数被调用前执行,因为new出来的结果作为构造函数的实参,但computePriority可能在这之前,之后,或者之间执行。也就是说,编译器可能按照这个执行顺序生成代码:

  1. 执行new Widget
  2. 执行computePriority
  3. 运行std::shared_ptr构造函数
    如果按照这样生成代码,并且在运行时computePriority产生了异常,那么第一步动态分配的Widget就会泄漏。因为它永远都不会被第三步的std::shared_ptr所管理了。
    使用std::make_shared可以防止这种问题。调用代码看起来像是这样:
    1
    2
    processWidget(std::make_shared<Widget>(),   //没有潜在的资源泄漏
    computePriority());
    在运行时,std::make_sharedcomputePriority其中一个会先被调用。如果是std::make_shared先被调用,在computePriority调用前,动态分配Widget的原始指针会安全的保存在作为返回值的std::shared_ptr中。如果computePriority产生一个异常,那么std::shared_ptr析构函数将确保管理的Widget被销毁。如果首先调用computePriority并产生一个异常,那么std::make_shared将不会被调用,因此也就不需要担心动态分配Widget(会泄漏)。
    当然除了使用std::make_shared<Widget>(),还有其他解法比如:
    1
    2
    std::shared_ptr<Widget> spw(new Widget);
    processWidget(spw, computePriority()); // 正确,但是没优化,见下
    但是能够一行代码解释清楚最好还是使用一行代码,所以还是建议使用make_shared

make_shared不适用场景

  1. 需要自定义删除器。make函数不能自定义删除器
  2. 需要精确精确的分配、释放对象大小的内存(重载了operator newoperator delete)的类,因为make_shared,因为make_shared使用std::allocate_shared进行内存分配,std::allocate_shared需要的内存总大小不等于动态分配的对象大小,还需要再加上控制块大小
  3. 对于大对象来说,std::weak_ptrs比对应的std::shared_ptrs活得更久的情况也不建议使用make_shared。因为make_shared申请的内存是包括资源和内存块的内容,只有最后一个std::shared_ptr和最后一个指向它的std::weak_ptr已被销毁,整块内存才会释放。而普通的方式创建的shared_ptr,最后一个shared_ptr被销毁就将对象的内存销毁了,控制块的还保留
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ReallyBigType { … };

auto pBigObj = //通过std::make_shared
std::make_shared<ReallyBigType>(); //创建一个大对象

//创建std::shared_ptrs和std::weak_ptrs
//指向这个对象,使用它们

//最后一个std::shared_ptr在这销毁,
//但std::weak_ptrs还在

//在这个阶段,原来分配给大对象的内存还分配着

//最后一个std::weak_ptr在这里销毁;
//控制块和对象的内存被释放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ReallyBigType { … };              //和之前一样

std::shared_ptr<ReallyBigType> pBigObj(new ReallyBigType);
//通过new创建大对象

//像之前一样,创建std::shared_ptrs和std::weak_ptrs
//指向这个对象,使用它们

//最后一个std::shared_ptr在这销毁,
//但std::weak_ptrs还在;
//对象的内存被释放

//在这阶段,只有控制块的内存仍然保持分配

//最后一个std::weak_ptr在这里销毁;
//控制块内存被释放

enable_share_from_this

实际开发中,有时候需要在类中返回包裹当前对象(this)的一个std::shared_ptr对象给外部使用,这个时候我们就需要enable_shared_from_this。

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
#include <iostream>
#include <memory>

class MyClass : public std::enable_shared_from_this<MyClass> {
public:
std::shared_ptr<MyClass> getShared() {
return shared_from_this();
}

void printMessage() {
std::cout << "Hello, this is MyClass!" << std::endl;
}
};

int main()
{
// 创建一个std::shared_ptr指向MyClass对象
std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>();

// 调用getShared()函数获取指向当前对象的std::shared_ptr
std::shared_ptr<MyClass> ptr2 = ptr->getShared();

// 使用ptr2调用对象的成员函数
ptr2->printMessage();

return 0;
}

源码分析:

enable_shared_from_this中有一个_Wptr,将_Wptrthis对应的share_ptr进行关联,从而后续通过shared_from_this从此_Wptr得到share_ptr

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 <class _Ty>
class enable_shared_from_this { // provide member functions that create shared_ptr to this
public:
// 在_Can_enable_shared中使用
using _Esft_type = enable_shared_from_this;

_NODISCARD shared_ptr<_Ty> shared_from_this() {
return shared_ptr<_Ty>(_Wptr);
}

_NODISCARD shared_ptr<const _Ty> shared_from_this() const {
return shared_ptr<const _Ty>(_Wptr);
}

_NODISCARD weak_ptr<_Ty> weak_from_this() noexcept {
return _Wptr;
}

_NODISCARD weak_ptr<const _Ty> weak_from_this() const noexcept {
return _Wptr;
}

protected:
constexpr enable_shared_from_this() noexcept : _Wptr() {}

enable_shared_from_this(const enable_shared_from_this&) noexcept : _Wptr() {
// construct (must value-initialize _Wptr)
}

enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept { // assign (must not change _Wptr)
return *this;
}

~enable_shared_from_this() = default;

private:
template <class _Yty>
friend class shared_ptr;
// 弱指针
mutable weak_ptr<_Ty> _Wptr;
};

那么这个是什么时候将_Wptrshare_ptr 关联的呢?是通过_Set_ptr_rep_and_enable_shared函数

1
2
3
4
5
6
7
8
9
10
explicit shared_ptr(_Ux* _Px) { // construct shared_ptr object that owns _Px
if constexpr (is_array_v<_Ty>) {
_Setpd(_Px, default_delete<_Ux[]>{});
} else {
_Temporary_owner<_Ux> _Owner(_Px);
// 此函数和enable_share_from_this相关
_Set_ptr_rep_and_enable_shared(_Owner._Ptr, new _Ref_count<_Ux>(_Owner._Ptr));
_Owner._Ptr = nullptr;
}
}

_Set_ptr_rep_and_enable_shared函数中会进行判断,如果是_Can_enable_shared就会关联

1
2
3
4
5
6
7
8
9
10
11
void _Set_ptr_rep_and_enable_shared(_Ux* const _Px, _Ref_count_base* const _Rx) noexcept { // take ownership of _Px
this->_Ptr = _Px;
this->_Rep = _Rx;
//在这个函数中
if constexpr (conjunction_v<negation<is_array<_Ty>>, negation<is_volatile<_Ux>>, _Can_enable_shared<_Ux>>) {
if (_Px && _Px->_Wptr.expired()) {
//将_Wptr和shared_ptr关联
_Px->_Wptr = shared_ptr<remove_cv_t<_Ux>>(*this, const_cast<remove_cv_t<_Ux>*>(_Px));
}
}
}

_Can_enable_shared的实现使用了SFINAE

1
2
3
4
5
6
7
8
template <class _Yty, class = void>
struct _Can_enable_shared : false_type {}; // detect unambiguous and accessible inheritance from enable_shared_from_this

template <class _Yty>
struct _Can_enable_shared<_Yty, void_t<typename _Yty::_Esft_type>>
: is_convertible<remove_cv_t<_Yty>*, typename _Yty::_Esft_type*>::type {
// is_convertible is necessary to verify unambiguous inheritance
};

线程安全

先说结论智能指针线程不安全。

share_ptr有两部分指向资源的指针 和指向控制块的指针,其中控制块部分中引用计数是原子的操作可以说它是安全的,其他部分都没有保障,比如指针的指向。

所以智能指针是线程不安全的

想要对智能指针安全操作可以参考muduo代码:

智能指针自己的实现

unique_ptr实现

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
template<typename T>
class KSmartPointUni
{
public:
KSmartPointUni();
KSmartPointUni(T* ptr);
~KSmartPointUni();
KSmartPointUni(KSmartPointUni&& ptr);
KSmartPointUni& operator=(KSmartPointUni&& ptr);

KSmartPointUni(const KSmartPointUni& ptr) = delete; // 不允许拷贝
KSmartPointUni& operator=(const KSmartPointUni& ptr) = delete;
T& operator*() const; // 返回*m_ptr对象
T* operator->() const; // 返回m_ptr对象
private:
T* m_ptr;
};

template<typename T>
inline KSmartPointUni<T>::KSmartPointUni() :m_ptr(nullptr)
{
}

template<typename T>
inline KSmartPointUni<T>::KSmartPointUni(T* ptr) : m_ptr(ptr)
{
}

template<typename T>
inline KSmartPointUni<T>::~KSmartPointUni()
{
if (m_ptr != nullptr) {
delete m_ptr;
}
}

template<typename T>
inline KSmartPointUni<T>::KSmartPointUni(KSmartPointUni<T>&& ptr)
{
m_ptr = ptr.m_ptr;
ptr.m_ptr = nullptr;
}

template<typename T>
inline KSmartPointUni<T>& KSmartPointUni<T>::operator=(KSmartPointUni&& ptr)
{
m_ptr = ptr.m_ptr;
ptr.m_ptr = nullptr;
return *this;
}

template<typename T>
inline T& KSmartPointUni<T>::operator*() const
{
return *m_ptr;
}

template<typename T>
inline T* KSmartPointUni<T>::operator->() const
{
return m_ptr;
}

share_ptr的实现

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
template<typename T>
class MySharedPtr
{
public:
MySharedPtr(T* ptr = nullptr); //构造
~MySharedPtr(); // 析构
MySharedPtr(const MySharedPtr<T>& sp);
MySharedPtr<T>& operator=(const MySharedPtr<T>& sp);
T& operator*(); // 返回*m_ptr对象
T* operator->(); // 返回m_ptr对象
int useCount();
T* get() const;
private:
// 采取引用计数
T* m_ptr;
// 控制块
int* m_count;
};

template<typename T>
inline MySharedPtr<T>::MySharedPtr(T* ptr) :m_ptr(ptr)
{
if (ptr == nullptr) {
m_count = new int(0);
}
else
{
m_count = new int(1);
}
}

template<typename T>
inline MySharedPtr<T>::~MySharedPtr()
{
(*m_count)--;
if ((*m_count) == 0)
{
if (m_count)
{
delete m_ptr;
m_ptr = nullptr;
}
if (m_count)
{
delete m_count;
m_count = nullptr;
}
}
}

template<typename T>
inline MySharedPtr<T>::MySharedPtr(const MySharedPtr<T>& sp)
{
m_ptr = sp.m_ptr;
m_count = sp.m_count;
(*m_count)++;
}

template<typename T>
inline MySharedPtr<T>& MySharedPtr<T>::operator=(const MySharedPtr<T>& sp)
{
if (this == &sp) return *this;
(*m_count)--;
if ((*m_count) == 0)
{
if (m_ptr)
{
delete m_ptr;
m_ptr = nullptr;
}
if (m_count)
{
delete m_count;
m_count = nullptr;
}
}
m_ptr = sp.m_ptr;
m_count = sp.m_count;
(*m_count)++;
return *this;
}

template<typename T>
inline T& MySharedPtr<T>::operator*()
{
return *m_ptr;
}

template<typename T>
inline T* MySharedPtr<T>::operator->()
{
return m_ptr;
}

template<typename T>
inline int MySharedPtr<T>::useCount()
{
return *m_count;;
}

template<typename T>
inline T* MySharedPtr<T>::get() const
{
return m_ptr;
}