版权声明:本文由博主@杨领以及发表在http://blog.csdn.net/yanglingwell,如果转载请注明出处。
STL中的智能指针(Smart Pointer)及其源码剖析: std :: auto_ptr
auto_ptr
是STL中的智能指针家族的成员之一,它管理由new expression
获得的对象,在auto_ptr
对象销毁时,他所管理的对象也会自动被delete
掉。auto_ptr
的拷贝构造函数和拷贝的赋值会改变右边的值,并且拷贝的副本不会等于原始的,被拷贝的那个auto_ptr
对象的值(实际上,auto_ptr
的拷贝构造函数和拷贝赋值函数会让左边的值接管右hand value所管理的对象。)- 由于不一样的拷贝语义,
auto_ptr
不适用于标准容器,因此,更建议使用std::unique_ptr
。
一. auto_ptr 的使用
1. auto_ptr 的声明
- 1
- 2
- 3
- 4
2. auto_ptr 的构造函数
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
(1) 构造 auto_ptr
对象, 让它管理 p
指向的对象。
(2) 构造 auto_ptr
对象,让它接管 r
管理的对象。实际上新的 auto_ptr
对象是靠 r.release()
函数获得管理权的。因此,r
失去了管理权。
(3) 这个构造函数和 (2) 类似, 主要针对能隐式转换为 T*
类型的 Y*
。
(4) 构造 auto_ptr
对象, 让它接管 auto_ptr_ref<Y>
类型的 m
管理的对象。而m
是通过 p.release()
从 auto_ptr
对象 p
中获取管理权的。
Q: what is auto_ptr_ref, what it achieves and how it achieves it ?
A: It is rather confusing. Basically, auto_ptr_ref exists because the auto_ptr copy constructor isn’t really a copy constructor in the standard sense of the word.
Copy constructors typically have a signature that looks like this:
X(const X &b);
The auto_ptr copy constructor has a signature that looks like this:X(X &b)
This is because auto_ptr needs to modify the object being copied from in order to set its pointer to 0 to facilitate the ownership semantics of auto_ptr.Sometimes, temporaries cannot match a copy constructor that doesn’t declare its argument const. This is where auto_ptr_ref comes in. The compiler won’t be able to call the non-const version of the copy constructor, but it can call the conversion operator. The conversion operator creates an auto_ptr_ref object that’s just sort of a temporary holder for the pointer. The auto_ptr constructor or operator = is called with the auto_ptr_ref argument.
如果您注意到,auto_ptr中的自动转换为auto_ptr_ref的转换运算符在源auto_ptr上执行释放,就像复制构造函数一样。
这是幕后发生的一种奇怪的小舞,因为auto_ptr修改了被复制的东西。
简单地总结:
auto_ptr_ref
主要解决用右值来构造auto_ptr
的情况。因为,auto_ptr(auto_ptr& r)
构造函数只能以左值引用做参数。当右值来构造auto_ptr_ref
的时候,实际上实现过程如下(这其实是移动语义的早期实现版本):
3. auto_ptr的析构函数:销毁管理的对象。
- 1
4. 拷贝赋值函数
- 1
- 2
- 3
- 4
- 五
- 6
- 7
- 8
auto_ptr
的拷贝赋值函数会让左边的值接管右边的值所管理的对象。
5. 隐式类型转换函数
- 1
- 2
- 3
- 4
- 五
- 6
- 7
(1)将该对象隐式转换为 auto_ptr_ref<Y>
类型。
(2)将该对象隐式转换为 auto_ptr<Y>
类型。
6.其他函数(auto_ptr :: get, auto_ptr :: operator *,auto_ptr :: operator->, auto_ptr :: reset, auto_ptr :: release)
- 1
- 2
- 3
- 4
- 五
- 6
- 7
- 8
- 9
- 10
(1)该报道查看 *this
所管理对象的指针。
(2)该报道查看 *this
所管理对象。
(3)该报道查看 *this
所管理对象的指针。
(4)让 *this
管理 p
所指向的对象,如果 *this
已有管理的对象,则先 delete
掉当前管理的对象。
(5)移交出 *this
所管理对象的管理权。返回 *this
所管理对象的指针,并将 *this
内部的指针置为空。
7.例子
-
代码
- 1
- 2
- 3
- 4
- 五
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 三十
- 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
-
运行结果:
二。auto_ptr源码剖析(源码出自VS2015)
1.辅助类 auto_ptr_ref
的源码
- 1
- 2
- 3
- 4
- 五
- 6
- 7
- 8
- 9
- 10
这个辅助类的源码比较简单,没有什么说的。前面也分析过了,这个辅助类其实是为了帮助 auto_ptr
完成右值引用传参而设计的。
2. auto_ptr
构造函数的源码
- 1
- 2
- 3
- 4
- 五
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
其中, auto_ptr
成员变量 _Ty *_Myptr
指向被它管理的对象。
(1)从原始指针中获取控制权。注意,由源码可知, auto_ptr
并没有将原始指针的控制权剥夺(从实现来看,也不能剥夺,因为 Ptr
不是指针引用,无法更改原始指针的指向),原始指针仍然保有对其资源的控制权但是,该资源的释放权实际上已经交给了。 auto_ptr
对象如:
- 1
- 2
- 3
- 4
- 五
- 6
(2)从 auto_ptr
对象中夺取对资源的控制权。由源码可知, _Right
将不再保有其资源的控制。注意,这里是左值引用参数,不能接收右值参数。
(3)与(2)类似。是针对可转换为 _Ty*
类型的 _Other*
类型的构造函数。
(4)这个构造函数的参数是 auto_ptr_ref<_Ty>
类型的。注意,它不是左值引用类型的参数,因此可以接收右值类型。这也是右值传参的必经之路。
3. auto_ptr
析构函数的源码
- 1
- 2
- 3
- 4
4. auto_ptr
拷贝赋值函数的源码
- 1
- 2
- 3
- 4
- 五
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
这里涉及到解决“自我赋值”(赋值自我)的问题,详情请参阅“有效的C ++”项目11:手柄分配到自我在运算符=
(1)将同类型的 _Right
管理的资源移交给 *this
。其中,reset(_Right.release())
先将 _Right
的资源以报道查看值的形式移交,然后设置给 *this
,这样就防止了“自我赋值”的时候出现问题。(如果 *this
就是 _Right
,那么执行完 _Right.release()
后, *this
管理的资源已经以返回值的形式移交出来作为参数,然后汉语中类似的 reset
给了自己)注意,这里是左值引用参数,不能接收右值参数。
(2)与(1)类似。的英文针对柯林斯转换为 _Ty*
类型的 _Other*
类型的拷贝产品函数。
(3)将一个 auto_ptr_ref
类型的变量赋值给 *this
,实际上是将资源的控制权移交给 *this
。这同样是为了传右值参数而设计的。这个函数体内冗余的代码同样是为了防止 _Right._Ref
等于 _Myptr
的情况。
5. auto_ptr
隐式类型转换函数
- 1
- 2
- 3
- 4
- 五
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
(1) auto_ptr
到 auto_ptr_ref
的隐式转换函数。由源码可知,该隐式转换也会剥夺 *this
对资源的管理权。这个转换虽然代码短小,但是能量巨大,右值类型的 auto_ptr
作参数传递时,全靠这个转换函数来起到周转的作用。当然,现在新的C ++标准有更好的方法来解决这个问题 - 右值引用。
(2)可转换的类型...
6. auto_ptr
其他函数
- 1
- 2
- 3
- 4
- 五
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 三十
- 31
- 32
(1)电子杂志 *this
所管理资源的指针,这个没什么说的。
(2)重载 operator*()
操作符,让 *this
有指针的行为。
(3)重载 operator->()
操作符,让 *this
有指针的行为。
(4)设置重新 *this
管理的资源,在当然此之前要将 *this
管理的资源释放掉。类似于 operator=
的检查,如果_Ptr
指向的资源就是 *this
管理的资源,就忽略这个操作。否则会提前释放资源。
(5) *this
移交出管理权,并将资源的指针返回。因此需要先记录下资源的地址,将然后 *this
指向资源的指针置为空,最后返回资源的地址。
三。总结
auto_ptr
用于RAII(资源获取初始化)思想实现对资源的管理(详情可参考“Effective C ++”Item 13:Use objects to manage resources)。但auto_ptr
属于该思想实现的早期版本,现在的标准库已经不推荐使用该工具了但是,了解。auto_ptr
的功能和实现还是有必要的,其一是,它相当于是其它更复杂智能指针的简化版本,源码简单,容易上手,对后面学习其它智能指针做铺垫; 其二是,学习 auto_ptr
可以让那个我们对RAII思想有所领悟。
四。参考文献
- Scott Meyers着,侯捷译“Effective C ++”
- cppreference.com
- VS2015相关源码