什么是RAII
RAII(Resource Acquisition Is Initialization)是由C++之父提出的,中文翻译为资源获取即初始化,使用局部对象来管理资源的技术称为资源获取即初始化;这里的资源主要是指操作系统中有限的东西如内存(heap)、网络套接字、互斥量、文件句柄等等,局部对象是指存储在栈的对象,它的生命周期是由操作系统来管理的,无需人工介入
RAII的原理
资源的使用一般经历三个步骤:
- 获取资源(创建对象)
- 使用资源
- 销毁资源(析构对象)
但是资源的销毁往往是程序员经常忘记的一个环节,所以程序界就想如何在程序中让资源自动销毁呢?解决问题的方案就是:RAII,它充分的利用了C++语言局部对象自动销毁的特性来控制资源的生命周期
裸指针存在的问题
1.难以区分指向的是单个对象还是一个数组
2.使用完指针之后无法判断是否应该销毁指针,因为无法判断指针是否”拥有“指向的对象
3.在已经确定需要销毁指针的情况下,也无法确定是用delete关键字删除,还是有其他特殊的销毁机制,例如通过将指针传入某个特定的销毁函数来摧毁指针
4.即使已经确定了销毁指针的方法,由于1的原因,仍然无法确定到底是i用delete(销毁单个对象)还是delete[](销毁一个数组)
5.假设上述的问题都解决了,也很难保证在代码的所有路径中(分支结构,异常导致的挑战),有且仅有一次销毁指针的操作;任何一条路径遗漏都可能导致内存的泄露,而销毁多次则会导致未定义行为
6.理论上没有方法来分辨一个指针是否处于悬挂状态
auto_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
|
class Object { int value; public : Object( int x = 0) :value(x) { cout << "Create Object:" << this << endl; } ~Object() { cout << "Destory Object:" << this << endl; } int & Value() { return value; } }; template < class _Ty> class my_auto_ptr { private : bool _Owns; _Ty* _Ptr; public : my_auto_ptr(_Ty* p = NULL) :_Owns(p != NULL), _Ptr(p) {} ~my_auto_ptr() { if (_Owns) { delete _Ptr; } _Owns = false ; _Ptr = NULL; } }; void fun() { my_auto_ptr<Object> obj( new Object(10)); } int main() { fun(); } |
在这里将Object构建完成后,将其指针给到p,当函数结束去调动智能指针的析构函数去释放空间
若我们需要在fun()函数中,去调用Object类的方法obj->Value();
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
|
class Object { int value; public : Object( int x = 0) :value(x) { cout << "Create Object:" << this << endl; } ~Object() { cout << "Destory Object:" << this << endl; } int & Value() { return value; } }; template < class _Ty> class my_auto_ptr { private : bool _Owns; _Ty* _Ptr; public : my_auto_ptr(_Ty* p = NULL) :_Owns(p != NULL), _Ptr(p) {} ~my_auto_ptr() { if (_Owns) { delete _Ptr; } _Owns = false ; _Ptr = NULL; } _Ty* get() const { return _Ptr; } _Ty& operator*() const { return *(get()); } _Ty* operator ->() const { return get(); } }; void fun() { my_auto_ptr<Object> obj( new Object(10)); cout << obj->Value() << endl; cout << (*obj).Value() << endl; } int main() { fun(); } |
通过运算符重载,(*obj) 后将直接指向堆区(heap)的对象实体
若我们通过一个my_auto_ptr去创建另一个my_auto_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
|
class Object { int value; public : Object( int x = 0) :value(x) { cout << "Create Object:" << this << endl; } ~Object() { cout << "Destory Object:" << this << endl; } int & Value() { return value; } }; template < class _Ty> class my_auto_ptr { private : bool _Owns; _Ty* _Ptr; public : my_auto_ptr(_Ty* p = NULL) :_Owns(p != NULL), _Ptr(p) {} ~my_auto_ptr() { if (_Owns) { delete _Ptr; } _Owns = false ; _Ptr = NULL; } my_auto_ptr( const my_auto_ptr& obj):_Owns(obj._Owns),_Ptr(obj._ptr) { } my_auto_ptr& operator=( const my_auto_ptr& _Y) { if ( this == &_Y) return * this ; if (_Owns) { delete _Ptr; } _Owns = _Y._Owns; _Ptr = _Y._Ptr; return 0; } _Ty* get() const { return _Ptr; } _Ty& operator*() const { return *(get()); } _Ty* operator ->() const { return get(); } void reset(_Ty* p = NULL) { if (_Owns) { delete _Ptr; } _Ptr = p; } _Ty* release() const { _Ty* tmp = NULL; if (_Owns) { ((my_auto_ptr*) this )->_Owns = false ; //常性进行修改 tmp = _Ptr; ((my_auto_ptr*) this )->_Ptr = NULL; } return tmp; } }; void fun() { my_auto_ptr<Object> pobja( new Object(10)); my_auto_ptr<Object> pobjb(pobja); } int main() { fun(); } |
如果通过浅拷贝,则两个指针拥有同一个资源,在析构的过程会造成资源的重复释放导致崩溃
若设置为将其资源进行转移
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
my_auto_ptr( const my_auto_ptr& obj):_Owns(obj._Owns),_Ptr(release()) { } my_auto_ptr& operator=( const my_auto_ptr& _Y) { if ( this == &_Y) return * this ; if (_Owns) { delete _Ptr; } _Owns = _Y._Owns; _Ptr = _Y.release(); return 0; } |
1
2
3
4
5
6
7
8
9
10
11
12
|
void fun(my_auto_ptr<Object> apx) { int x = apx->Value(); cout<<x<<endl; } int main() { my_auto_ptr<Object> pobja( new Object(10)); fun(pobja); int a = pobja->Value(); cout<<a<<endl; } |
那么上面的过程中,资源会进行转移pobja将不再拥有资源,导致pobja失去资源进而程序崩溃
这也就是auto_ptr的局限性,也导致该智能指针的几乎没有使用
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
|
class Object { int value; public : Object( int x = 0) :value(x) { cout << "Create Object:" << this << endl; } ~Object() { cout << "Destory Object:" << this << endl; } int & Value() { return value; } }; int main() { std::unique_ptr<Object> pobja( new Object(10)); //std::unique_ptr<Object> pobjb(pobja); error //不允许 std::unique_ptr<Object> pobjb(std::move(pobja)); } |
通过移动赋值是可以的,通过明确的概念,对其资源进行转移
同时unique_ptr可以区分其所指向的是一个单独空间,或者是连续的空间
1
2
3
4
5
6
7
8
9
10
11
12
13
|
struct delete_ar_object { void operator()(Object* op) { if (op == NULL) return ; delete [] op; } } int main() { std::unique_ptr<Object> pobja( new Object(10)); std::unique_ptr<Object,delete_ar_object> pobjb( new Object[10]); } |
在这里如果是连续空间,会调用删除连续空间的删除器;单独空间则使用默认删除器
unique_ptr在编写的时候,有多个模板类,分别对应单个对象的方案和一组对象的方案
并且可以通过智能指针指向fopen打开的文件对象,而文件对象是同fclose去进行关闭的
1
2
3
4
5
6
7
8
9
|
struct delete_file { void operator()( FILE *fp) { if (fp == NULL) return ; fclose (fp); } } std::unique_ptr< FILE ,delete_file> pfile( fopen ( "zyq.txt" , "w" )); |
这里只需要将默认的删除器,更改为对文件对象的删除器
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注服务器之家的更多内容!
原文链接:https://blog.csdn.net/XXXTENTAC1ON/article/details/123824606