MargaretChen
C++智能指针

C++智能指针

参考链接

裸指针

非动态申请内存地址,函数运行结束编译器自动释放

1
2
3
4
void func(){
int a = 100;
int* p = &a;
}

动态申请内存地址,函数结束也不会释放

但是也无法访问该内存地址,造成内存泄漏

1
2
3
4
void func1(){
int *p = new int;
//delete p;
}

c++为了追求性能,没有在语言层面上的垃圾回收功能

shared_ptr

共享指针会记录有多少个共享指针指向同一个物体

当数字降为0时,程序会自动释放这个物体

a01.png

使用智能指针需要加上

1
#include <memory>

创建一个class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Ball{
public:
Ball(){
cout << "A ball appears" << endl;
}

~Ball(){
cout <<"A ball disappears" <<endl;
}

void Bounce(){
cout <<"A ball jumps" << endl;
}
};

创建三个shared_ptr指向Ball实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main(){

shared_ptr<Ball> p = make_shared<Ball>();
cout << p.use_count() << endl;

shared_ptr<Ball> p2 = p;
cout << p.use_count() << " " <<p2.use_count() << endl;

shared_ptr<Ball> p3 = p;
cout << p.use_count() << " " << p2.use_count() << " " <<p3.use_count() << endl;

p.reset();
cout << p.use_count() << " " << p2.use_count() << " " <<p3.use_count() << endl;
p2.reset();
cout << p.use_count() << " " << p2.use_count() << " " <<p3.use_count() << endl;
p3.reset();
cout << p.use_count() << " " << p2.use_count() << " " <<p3.use_count() << endl;


return 0;
}

运行结果如下
a01.png

可以看到当所有shared_ptr都重置后

原指针指向的Ball就自动释放了

引用计数

创建一个共享指针指向物体,引用计数会计算当前指向该物体的共享指针个数。

当引用计数为0时,程序自动释放这个物体,防止出现内存泄漏

同时使用裸指针

如果仍然需要获得指向物体的裸指针可以使用get()方法

1
Ball* rp = p.get();

但是当所有共享指针被销毁时,裸指针仍然存在,资源也会释放

应尽量避免裸指针和共享指针混用

unique_ptr

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
#include<iostream>
#include<memory>
using namespace std;

class Ball{
public:
Ball(){
cout << "A ball appears" << endl;
}

~Ball(){
cout <<"A ball disappears" <<endl;
}

void Bounce(){
cout <<"A ball jumps" << endl;
}
};

void func(){

cout << "Now in func" <<endl;

unique_ptr<Ball> up {make_unique<Ball>()};
up->Bounce();

cout << "Quit func" <<endl;
}

int main(){

cout << "Now in main" <<endl;
func();
cout << "Quit main" <<endl;
return 0;
}

运行结果如下:

a01.png

unique_ptr销毁时资源会自动进行释放

其他用法:

get()reset()用法和shared_ptr一致

可以使用release()和已绑定资源解绑

release()返回资源裸指针

1
2
3
4
//release()用法
Ball* ball = up.release();
delete ball;
ball = nullptr

使用release()move()转移控制权

1
2
3
4
unique_ptr<int> up1 = make_unique<int>(100);
unique_ptr<int> up2(up1.release());
//使用move()
unique_ptr<int> up2 = move(up1);

weak_ptr

shared_ptr的伴侣,它是shared_ptr的观察者,对资源的引用为非拥式

没有资源的管理权限,不能控制资源释放

访问资源的时候需要通过shared_ptr

weak_ptr仅用于检查资源是否存在,不用于控制资源

使用场景

环形依赖的情况下,使用shared_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
//环形依赖
#include<iostream>
#include<memory>
using namespace std;

struct School;

struct Teacher{
string name;
int age;
shared_ptr<School> school;
~Teacher(){
cout << "Teacher Destructed." <<endl;
}
};

struct School(){
string name;
shared_ptr<Teacher> principal;
~
}

int main(){
auto principal = make_shared<Teacher>();
auto university = make_shared<School>();

principal->school = university;
university->principal = principal;
return 0;
}

a01.png

运行程序可以看到控制台什么都没有打印,

说明该对象在程序运行结束时没有被销毁

原因:因为当程序执行到principal->school = university;

下面university中的shared_ptr仍然指向principal

另一个对象同理,他们的引用计数都没有降为0,所以不会自动销毁

使用weak_ptr解决环形依赖问题

只需要将Teacher 类中的 shared_ptr改为 weak_ptr即可

运行程序可以看到两个对象被正常释放

a01.png

注意事项

智能指针是C++11的新功能

需要在编译器加入C++11命令
a01.png

Author:MargaretChen
Link:http://margaret-chen217.github.io/2023/03/26/C++智能指针/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可