深度剖析智能指针
RALL:资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数中完成资源的分配和初始化,在析构函数中完成资源的清理。
首先来看这样一个例子:
此例子乍一看上去,new/delete匹配,并没有什么错。但就因为return的存在,使得Test()函数提前结束,并没有执行delete p。这就使得内存泄露。
内存泄露的危害:使得可用内存越来越少,下一次开辟可能不够,程序崩溃。
为解决此类问题,引入智能指针。
所谓的智能指针就是智能/自动化的管理指针所指向的动态资源的释放。
智能指针的行为类似于常规指针,重要的区别在于它负责自动释放所指向的对象。
智能指针也是模板,当我们创建一个智能指针时,需要提供指针可以指向的类型。
智能指针有:auto_ptr,scoped_ptr,shared_ptr,scoped_array,shared_array
1.auto_prt的模拟实现(以前)
注:以前的auto_ptr看起来已经实现的挺好的,多个指针指向同一块内存,并且释放的时候也正确。
但是在实现的时候,会出现"野指针"。也就是所谓的"悬挂指针"。
在析构函数中,只是对拥有管理权者进行了析构。在delete内存之后重置指针的方法只对这个指针有效,对其他任何指向(已释放的)内存的指针是没有作用的。所以会出现"野指针"。
为解决"野指针"问题,可以将管理权转交后,将以前的指针赋为NULL。也就是auto_ptr的现代写法。
auto_ptr的现代写法:
注:虽然此方法解决了"野指针"的问题,但是在拷贝构造和赋值后,只有一个指针有效。其他的都已为NULL,但有时候会对NULL指针进行引用,使得程序崩溃。
2.简单粗暴的scoped_ptr
scoped_ptr不允许拷贝构造和赋值,在C++11标准中叫unique_ptr
注:为了防拷贝和赋值
(1)只将拷贝构造和赋值函数声明不定义
(2)将拷贝构造和赋值函数声明为private或protected,防止别人搞破坏
3.允许拷贝和赋值的shared_ptr
shared_ptr是通过一个引用计数来记录该块空间被共享了几次,只有当计数器为0时才被释放。
4. 简单粗暴的scoped_array
scoped_array和scoped_ptr类似,是为了防拷贝和赋值。但是scoped_array是对数组来说的,因此在析构时要特别注意,应该使用delete[]来释放内存。
注:scoped_ptr是为了防拷贝和赋值。因此只将拷贝和赋值声明而不定义,并且将拷贝和赋值声明为private或者protected,防止他人搞破坏。
5.允许拷贝和赋值的shared_array
shared_array和shared_ptr类似。也是使用了一个引用计数。但是shared_array是对数组来说的。因此在析构时应使用delete[]。
6.现代版的shared_ptr看起来不错,但是会出现以下几个问题
(1)循环引用
(2)定置删除器
6.1循环引用
为了解决循环引用,应该使用弱指针weak_ptr(防止引用计数的增加)
注:所谓的weak_ptr就是用来服务shared_ptr。将一个weak_ptr绑定一个shared_ptr上不会改变shared_ptr的引用计数。
6.2 定置删除器
我们都只到shared_ptr只能释放new开辟出来的空间。而对于malloc开辟出来的空间,以及fopen打开的文件不能处理。为了能够处理各种情况,引入定置删除器。而定置删除器的实现是通过operator()即重载()来实现的。
测试结果:
注:任何情况下都不要使用auto_ptr。