目录
1. 为什么需要智能指针?
看下面的例子:
若p1处new抛异常,则相当于p1的new没有成功,则什么都不用做
若p2处new抛异常,则相当于p2的new没有成功,而p1的new成功了,所以需要释放p1,然后再重新抛出
若div处抛异常,则将p1与p2都释放,再将其重新抛出
可以看出处理起来非常麻烦,存在内存泄漏的问题(只进行new,但没有delete)
第二个new抛异常要释放第一个new,div抛异常要释放前两个new
若再添加一个new,则又会存在new抛异常的问题,还需添加 try catch
为了提前预防内存泄漏的问题,就提出了智能指针
2. 智能指针的使用
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
不需要显式地释放资源
对象所需的资源在其生命期内始终保持有效
RAII是一种思想,智能指针是这种思想的产物
智能指针的常见问题
1.使用对象的生命周期去控制资源
创建一个私有的成员变量 _ptr指针
在构造函数时,将指针保存起来
在析构函数时,将指针释放
将申请的资源,交给智能指针对象去管理
(通过这个指针 去构造一个智能指针对象,这个对象会把指针保留起来)
创建对象时,会调用构造函数,将new int 传给类中的指针,对象会把指针保留起来
v1和v2属于局部对象,出了作用域时,就会调用析构函数 ,完成释放
若第一个new抛异常,就不会进入构造函数中
若第二个new抛异常,则调用析构,将第一个new释放掉
若div抛异常,则v1和v2对象都调用析构,将第一个new和第二个new都释放掉
通过类的构造和析构的自动调用,利用对象的生命周期来管理资源,被称之为 RAII
2. 像指针一样使用
在类中实现 operator() 和operator->,使对象可以进行解引用 和->访问成员的操作
3. 拷贝问题
因为没有在类中实现拷贝构造,默认是浅拷贝 ,所以就会导致释放两次,从而报错
深拷贝是不可以的,因为指针拷贝要的就是浅拷贝
链表等迭代器 结构与智能指针类似,用的是浅拷贝,为什么没有问题?
因为迭代器不管资源的释放,资源释放是容器处理的
智能指针需要管资源释放,所以不能单纯的浅拷贝
auto_ptr ——管理权转移
当上述v1和v2都管理这个资源就会有问题,两者都会去释放,导致释放两次
所以C++98版本的库中就提供了auto_ptr的智能指针,提出了 管理权转移的思想
将管理权只给一个对象,剩下一个对象去除管理权
如果不了解管理权转移的特性,但管理权转移,存在被拷贝对象悬空的问题
unique_ptr ——防拷贝
使用禁止生成默认函数的关键字 delete
shared_ptr (根本解决拷贝问题)
特点为使用引用计数,支持拷贝
有两个对象指向资源,当析构时,会析构两次
为了解决这个问题,就增加一个引用计数,若只有一个对象,就为1,若为两个对象,则为2
当其中一个对象要析构时,就先看引用计数,若引用计数减1还大于0,就要什么都不管
若引用计数减1为0