-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathenable_shared_from_this.txt
193 lines (161 loc) · 6.41 KB
/
enable_shared_from_this.txt
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
enable_shared_from_this类的作用
有时候我们需要在一个被 shared_ptr 管理的对象的内部获取自己的 shared_ptr
struct Bad {
void fun() {
shared_ptr<Bad> sp{this};
cout<<sp->count()<<endl;
}
};
但是注意, 在 func 函数构造智能指针时, 我们无法确定这个对象是不是被 shared_ptr 管理着, 因此这样构造的 shared_ptr 并不是与其他 shared_ptr 共享一个计数器, 那么, 在析构时就会导致对象被重复释放, 从而引发错误.
现在明确一下我们的需求: 在一个对象内部构造该对象的 shared_ptr 时, 即使该对象已经被 shared_ptr 管理着, 也不会造成对象被两个独立的智能指针管理.
这就要求我们在对象内构造对象的智能指针时, 必须能识别有对象是否已经由其他智能指针管理, 智能指针的数量, 并且我们创建智能指针后也能让之前的智能指针感知到.
正确做法是继承 enable_shared_from_this 类, 调用 shared_from_this() 函数生成 shared_ptr
#include <memory>
#include <iostream>
class A
{
public:
A()
{
std::cout << "A()" << std::endl;
}
~A()
{
std::cout << "~A()" << std::endl;
}
std::shared_ptr<A> getSharedPtr()
{
std::shared_ptr<A> ptr(this);
return ptr;
}
};
int main()
{
std::shared_ptr<A> ptr1(new A());
std::shared_ptr<A> ptr2 = ptr1->getSharedPtr();
return 0;
}
A()
~A()
~A()
#include <memory>
#include <iostream>
class A
{
public:
A(): mPtr(new int(10))
{
std::cout << "A(), *mPtr = " << *mPtr <<std::endl;
}
~A()
{
std::cout << "~A()" << std::endl;
delete mPtr;
mPtr = nullptr;
}
std::shared_ptr<A> getSharedPtr()
{
std::shared_ptr<A> ptr(this);
return ptr;
}
private:
int *mPtr;
};
int main()
{
std::shared_ptr<A> ptr1(new A());
std::shared_ptr<A> ptr2 = ptr1->getSharedPtr();
return 0;
}
free 了无效的指针导致程序崩溃。我们应该能看到是重复释放的问题,这里我们将裸指针赋给了智能指针,这样做潜在的危险就是对象被多次释放。
打印出智能指针的引用计数:
std::cout << ptr1.use_count() << std::endl;
std::cout << ptr2.use_count() << std::endl;
智能指针 ptr1 和 ptr2 的引用计数都是 1
说明它们指向的不是同一个资源(这里可能会有人疑惑两个智能指针指向同一个地址,怎么引用计数没有增加呢?而且在 shared_ptr 的实现中,是不是只在拷贝构造和赋值操作operator=才会增加引用计数呢?)
所以这里两个智能指针,释放两次,而这两次释放的是同一个地址,所以崩溃了。
那 enable_shared_from_this 的作用就是解决这个问题
class A: public std::enable_shared_from_this<A>
{
public:
A()
{
std::cout << "A()" << std::endl;
}
~A()
{
std::cout << "~A()" << std::endl;
delete ptr;
ptr = nullptr;
}
std::shared_ptr<A> getSharedPtr()
{
return shared_from_this();
}
private:
int *ptr;
};
int main()
{
std::shared_ptr<A> ptr1(new A());
std::shared_ptr<A> ptr2 = ptr1->getSharedPtr();
std::cout << ptr1.use_count() << std::endl;
std::cout << ptr2.use_count() << std::endl;
return 0;
}
两个智能指针的引用计数都为2,这两个智能指针指向了相同的资源,在 main 函数退出后,两个智能指针释放,引用计数变为 0,资源释放正常。
所有的shared_ptr都是强引用(strong references)
引用计数的增加:
1.用已经定义了的shared_ptr来初始化别的shared_ptr时,引用计数会增加
auto p1 = make_shared<int>(100);
auto p2(p1);//引用计数+1
//p1和p2都指向heap区的值为100的int型变量的内存地址
2.把智能指针当做实参往函数中传递时,函数的非引用类型的shared_ptr形参会让引用计数+1
当调用完该函数时,引用计数会自动减1,因为此时该shared_ptr形参所指向的内存会给自动释放掉
void testfunc(shared_ptr<int> sp) {
cout << *sp << endl;
}
int main(void) {
auto p1 = make_shared<int>(100);
auto p2(p1);
testfunc(p1);//引用计数先+1 后-1
return 0;
}
3.当shared_ptr作为函数的返回值并用一个对应的shared_ptr接住该函数返回值时,引用计数会增加
如果你没有用一个shared_ptr来接住这个函数返回值的话,该返回的临时shared_ptr指针对象就会给编译器释放掉!这样一增一减引用计数就相当于不变了
shared_ptr<int> createShared_ptr(shared_ptr<int>& sp) {
return sp;
}
int main(void) {
auto p1 = make_shared<int>(100);
auto p2(p1);
auto p3 = createShared_ptr(p1);//引用计数+1
createShared_ptr(p1);//引用计数先+1 后-1 ==> 引用计数不变了!
//因为你没有用一个shared_ptr指针来接住这个函数的返回值!
return 0;
}
引用计数的减少:
当shared_ptr指针对象的引用计数从1->0时,该指针所指向的原对象的内存空间就会给释放掉
1.让已经定义了的shared_ptr指向一个新的对象时,引用计数会减少
auto p1 = make_shared<int>(100);
auto p2(p1);
auto p3 = createShared_ptr(p1);
p3 = make_shared<int>(888);
//让p3重新指向新的对象,此时p3这个shared_ptr的引用计数就为1
//而p1和p2这2个shared_ptr的引用计数就减为2(原本为3)
p2 = make_shared<int>(88);
//让p2重新指向新的对象,此时p2这个shared_ptr的引用计数就为1
//而p1这个shared_ptr的引用计数就减为1(原本为3)
p1 = make_shared<int>(8);
//让p1重新指向新的对象,此时p1这个shared_ptr的引用计数就为1
//而p1这个shared_ptr的指向的原对象的内存就会给释放掉
//为什么会释放呢?
//答:因为当shared_ptr指针对象的引用计数从1-》0时,该指针所指向的原对象的内存空间就会给释放掉~
2.局部的shared_ptr离开其作用域时,引用计数会减少
3.当一个shared_ptr的引用计数从1->0时,则该智能指针会自动释放自己所管理(指向)的对象
auto ps1 = make_shared<int>(10);//ps1指向一个对象
auto ps2 = make_shared<int>(10);//ps2指向另一个对象
ps1 = ps2;
//此时,给ps1赋值会让ps1指向ps2
//此时,ps1和ps2都指向同一个对象了,那么此时这2个shared_ptr指针的引用计数就为2
//ps1原来所指向的对象的引用计数就会从1->0,因为你不指向原对象了,就自动给你释放掉了