智能指针
C++ 中的指针分为原始指针和智能指针
智能指针是裸指针的封装
智能指针只解决一部分问题:独占、共享所有权指针的释放、传输,没有从根本上解决C++内存安全的问题
独占指针:unique_ptr
特点:
- 在任何时刻,只能有一个指针管理内存
- 指针超出作用域,内存将自动释放
- 该类型指针不可以copy,只可以move
三种创建方式
- 通过已有裸指针创建
- 通过
new
来创建 - 通过
std::make_unique
创建(推荐)
unique_ptr
可以通过get()
来获取地址
unique_ptr
实现了->
和*
- 可以通过
->
调用成员函数 - 可以通过
*
调用dereferencing
解引用
代码实例unique
指针的三种创建方式
使用原始指针:注意将原始指针置空
1
2
3
4Cat *c_ptr=new Cat("aa");
unique_ptr<Cat> u_c_ptr(c_ptr);
//之后要将原始指针置空
c_ptr=nullptr;使用new
1
unique_ptr<Cat> u_ptr(new Cat("bb"));
使用
make_unique
(推荐)1
unique_ptr<Cat> u_ptr=make_unique<Cat>("cc");
程序结束会后自动调用析构函数!
其他:使用get()
获取地址,*
解引用
1 | unique_ptr<int> u_ptr=make_unique<int>(10); |
1 | 10 |
unique_ptr与函数调用
unique_ptr
是不可copy,只可以move- 作函数参数和函数返回值时要注意所有权
passing by value
1
2
3
4
5
6
7
8
9
10
11
12void pass_by_value(unique_ptr<Cat> p){
p->cat_info();
}
int main()
{
unique_ptr<Cat> c=make_unique<Cat>("Tom");
pass_by_value(move(c));
// 此时c已经被move到pass_by_value中,c已经不可用
//c->cat_info();此时程序会崩溃
pass_by_value(make_unique<Cat>("Jerry"));//默认调用move
return 0;
}1
2
3
4
5
6Constructor of Cat:Tom
cat info name:Tom
Desctructor of Cat:Tom
Constructor of Cat:Jerry
cat info name:Jerry
Desctructor of Cat:Jerrypassing by reference
- 不加const的情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15void pass_by_reference(unique_ptr<Cat> &p)
{
p->set_name("Amy");
p->cat_info();
p.reset();// 清空
}
int main()
{
unique_ptr<Cat> cat = make_unique<Cat>("Tom");
pass_by_reference(cat);
cout << "=============unique_ptr=============" << endl;
// 此时cat被清空了
cout << "cat address:" << cat.get() << endl;
return 0;
}1
2
3
4
5Constructor of Cat:Tom
cat info name:Amy
Desctructor of Cat:Amy
=============unique_ptr=============
cat address:0 - 加const的情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15void pass_by_reference(const unique_ptr<Cat> &p)
{
p->set_name("Amy");
p->cat_info();
// p.reset();加了const以后不允许清空
//但还是可以对对象进行修改
}
int main()
{
unique_ptr<Cat> cat = make_unique<Cat>("Tom");
pass_by_reference(cat);
cout << "=============unique_ptr=============" << endl;
cout << "cat address:" << cat.get() << endl;
return 0;
}1
2
3
4
5Constructor of Cat:Tom
cat info name:Amy
=============unique_ptr=============
cat address:0x1f3f60
Desctructor of Cat:Amy
- 不加const的情况
return by value
1
2
3
4
5
6
7
8
9
10
11
12unique_ptr<Cat> get_unique_ptr()
{
unique_ptr<Cat> p = make_unique<Cat>("Local cat");
cout << "unique_ptr addreess:" << p.get() << endl;
return p;
}
int main()
{
cout<<get_unique_ptr().get()<<endl;
cout << "=============unique_ptr=============" << endl;
return 0;
}1
2
3
4Constructor of Cat:Local cat
unique_ptr addreess:0x1043f60
0x1043f60
Desctructor of Cat:Local cat
共享指针:shared_ptr
shared_ptr
又称计数指针- 与
unique_ptr
不同的是它可以共享数据,可以copy shared_ptr
创建了一个计数器,与类对象的内存相关联;copy则计数器+1,销毁则计数器-1;api为use_count()
- 常量类型
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// 常量类型
// 还有一种定义方式
shared_ptr<int> ptr(new int(10));
shared_ptr<int> p1 = make_shared<int>(10);
cout << "value:" << *p1 << endl;
cout << "use count:" << p1.use_count() << endl;
// 复制,引用计数+1
shared_ptr<int> p2 = p1;
shared_ptr<int> p3 = p1;
cout << "use count:" << p1.use_count() << endl;
cout << "use count:" << p2.use_count() << endl;
cout << "use count:" << p3.use_count() << endl;
// 修改值,另一个值也会改变,因为指向一个地方
*p3 = 30;
cout << "p1 value:" << *p1 << endl;
cout << "p2 value:" << *p2 << endl;
// 清空一个
p3 = nullptr;
cout << "use count:" << p1.use_count() << endl;
cout << "use count:" << p2.use_count() << endl;
cout << "use count:" << p3.use_count() << endl; - 自定义类型
1
2
3
4
5
6
7
8
9
10
11
12
13shared_ptr<Cat> cat1 = make_shared<Cat>();
cout << "cat use count:" << cat1.use_count() << endl;
shared_ptr<Cat> cat2 = cat1;
shared_ptr<Cat> cat3 = cat2;
cout << "cat use count:" << cat1.use_count() << endl;
cout << "cat use count:" << cat2.use_count() << endl;
cout << "cat use count:" << cat3.use_count() << endl;
cat1.reset();
cout << "cat use count:" << cat1.use_count() << endl;
cout << "cat use count:" << cat2.use_count() << endl;
cout << "cat use count:" << cat3.use_count() << endl;
cout << "==================================" << endl;
return 0;引用计数不为0,程序结束之后销毁1
2
3
4
5
6
7
8cat use count:3
cat use count:3
cat use count:3
cat use count:0
cat use count:2
cat use count:2
==================================
Desctructor of Cat:Mimi1
2
3
4
5
6
7
8
9
10
11
12
13
14
15shared_ptr<Cat> cat1 = make_shared<Cat>();
cout << "cat use count:" << cat1.use_count() << endl;
shared_ptr<Cat> cat2 = cat1;
shared_ptr<Cat> cat3 = cat2;
cout << "cat use count:" << cat1.use_count() << endl;
cout << "cat use count:" << cat2.use_count() << endl;
cout << "cat use count:" << cat3.use_count() << endl;
cat1.reset();
cout << "cat use count:" << cat1.use_count() << endl;
cout << "cat use count:" << cat2.use_count() << endl;
cout << "cat use count:" << cat3.use_count() << endl;
cat2.reset();
cat3.reset();
cout << "==================================" << endl;
return 0;引用计数为0,就调用析构函数1
2
3
4
5
6
7
8
9cat use count:1
cat use count:3
cat use count:3
cat use count:3
cat use count:0
cat use count:2
cat use count:2
Desctructor of Cat:Mimi
==================================
shared_ptr和函数调用
shared_ptr passed by value
涉及copy,内部引用计数+1,但离开函数use_count-1shared_ptr passed by ref
return by value
1 | void pass_by_value(shared_ptr<Cat> cat){ |
1 | Constructor of Cat:cat1 |
shared_ptr与unique_ptr
- 不能将
shared_ptr
转换为unique_ptr
unique_ptr
可以转换为shared_ptr
- 通过
std::move
- 通过
1 | unique_ptr<Cat> get_unique_ptr() |
weak_ptr
weak_ptr
并不拥有内存的所有权,所以不能调用->
和*
解引用- 和
shared_ptr
类似,但不影响对象引用计数 - 为什么需要
weak_ptr
:解决shared_ptr
循环引用的问题- 例:Person1中有一个成员friend,指向另一个Person2,Person2->friend又指向另一个Person3或者指向Person1,最后每个的引用计数都不为0 —— 循环引用
- 这里需要用一个不需要拥有所有权的指针
1 | int main() |
循环依赖的问题
1 |
|
1 | Constructor of Cat:cat1 |
发现没有调用cat3和cat4的析构函数
解决方法:将成员变量数据shared_ptr<Cat> m_friend;
改为weak_ptr<Cat> m_friend;
但是void set_friend(shared_ptr<Cat> m_friend)
这里的不用改
1 | Constructor of Cat:cat1 |
此时可以正常析构