Shared Pointer 智能指针和动态内存

C++11 引入的智能指针及其搭配的动态内存申请方法,在我看来是new的加强版,它不仅速度更快,而且不需要用户考虑内存销毁。
本文通过小例子回顾一下各种内存管理的效果。

1) 普通情况
函数内的变量a是创建在【栈】里,即使其指针随函数返回,但变量内存会随着函数的销毁而出栈销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// main.cpp
#include <iostream>
using namespace std;
int* func(){
int a = 1;
return &a;
}
int main(){
int *ptr = func();
cout << *ptr <<endl;
}

===build and run===
$ g++ main.cpp
main.cpp: In function ‘int* func()’:
main.cpp:5:9: warning: address of local variable ‘a’ returned [-Wreturn-local-addr]
5 | return &a;
| ^~
main.cpp:4:6: note: declared here
4 | int a = 1;
| ^
$ ./a.out
Segmentation fault (core dumped)

2) 使用static, 变量a放置在【静态区】,不会随着函数出栈而销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// main_static.cpp 
#include <iostream>
using namespace std;
int* func(){
static int a = 1;
return &a;
}
int main(){
int *ptr = func();
cout << *ptr <<endl;
}

===build and run===
$ g++ main_static.cpp
$ ./a.out
1

3) 使用new, 变量a放置在【堆】中,不会随着函数出栈而销毁,但最后要记得主动用delete销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// main_new.cpp 
#include <iostream>
using namespace std;
int* func(){
int* a = new int(1);
return a;
}
int main(){
int *ptr = func();
cout << *ptr <<endl;
delete ptr;
}

===build and run===
$ g++ main_new.cpp
$ ./a.out
1

4) 使用智能指针shared_ptr和动态内存make_shared,变量a仍然放置在【堆】中,但不需要主动销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// main_shared.cpp 
#include <iostream>
#include <memory>
using namespace std;
shared_ptr<int> func(){
shared_ptr<int> a = make_shared<int>(1);
return a;
}
int main(){
shared_ptr<int> ptr = func();
cout << *ptr <<endl;
}

===build and run===
$ g++ main_shared.cpp
$ ./a.out
1

5) 使用auto对4)进行简化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// main_shared_auto.cpp 
#include <iostream>
#include <memory>
using namespace std;
auto func(){
auto a = make_shared<int>(1);
return a;
}
int main(){
auto ptr = func();
cout << *ptr <<endl;
}
===build and run===
$ g++ main_shared_auto.cpp
$ ./a.out
1

以上代码的递进无须多加解释,就能体会智能指针的益处。
不过上面的例子都是关于智能指针在一开始申请动态内存时就绑定好的情况, 下面的例子将展示一个已经存在的动态内存“半路”赋予给智能指针进行管理的优势

6) 让智能指针作对象的管家

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// main_obj.cpp 
#include <iostream>
#include <memory>
using namespace std;
class dorian{
public:
int *ptr;
dorian():ptr(new int(1)){}
~dorian(){
cout << "running ~dorian()"<<endl;
delete ptr;
ptr=nullptr;
}
};
int main(){
dorian* obj = new dorian();
shared_ptr<dorian> sptr(obj);
}
===build and run===
$ g++ main_obj.cpp
$./a.out
running ~dorian()

可见,一个new出来的动态内存或者对象(无论此对象是否定义了动态内存),是可以传给shared_ptr的。shared_ptr会帮你管理此对象,即使你忘了析构,它也会帮你析构(相当于帮你执行delete obj)
总之,使用智能指针是一个好习惯。

以上。