)
概念正如上一篇所说如果要对可能发生异常的都用catch(...)捕获一下那代码就会非常冗余如new可能也会抛出异常。这就是智能指针出现的缘由RAII是一种管理资源的类的设计思想——利用对象的生命周期管理指定动态资源内存、文件指针、网络连接、互斥锁等以防资源泄漏。详细点说就是RAII通过得到动态资源时将资源委托给一个对象保持资源在对象的生命周期内始终保持有效当对象生命周期结构系统自动调用的析构函数中释放资源有效防止了资源的泄漏问题原理C实现的智能指针在C标准库的memory中我们一起来学习一下在学习之前我们需要先明确我们会有暂时持有访问某个资源的需求如需要智能指针对象为实参要传给一个函数的形参以访问那个资源。如果我们只传这个指向资源的指针或者智能指针引用都无法覆盖所有场景需要考虑到智能指针的拷贝是深拷贝还是浅拷贝呢因为我们是为了管控资源肯定不可以深拷贝如果浅拷贝拷贝出来的对象析构那实参的对象再析构就会报错了首先是auto_ptr它是C98提出来的设计的比较糟糕当这个智能指针要拷贝另一个对象时会直接将管理权送给复制的对象相当于被复制的对象悬空了原auto_ptr类对象内部就变成了空指针我们无法再用。设计的十分糟糕在C11出现之前很多公司就明令禁止auto_ptrC11也增加了新的智能指针语法这个语法就不要使用了unique_ptr智能指针对象不能拷贝unique的汉语是唯一只能移动。如果不需要拷贝时建议使用shared_str智能指针对象可以拷贝也可以移动shared的汉语是共享。需要拷贝时就使用它。它的底层是增加了引用计数这一概念实现了多智能指针共同管理统一资源。引用计数——如果多一个对象指向此资源这个引用计数就如果此对象析构了引用计数--直到引用计数等于0时此时没有对象指向此资源这个资源就要释放weak_ptr创造出来是为了解决shared_ptr的一个问题文章后面再介绍它不是智能指针它不能管理资源智能指针默认是通过delete来释放资源但也可能会有其他形式的动态资源如C语言中的malloc等函数和文件操作、以及new[]都无法简单通过delete来释放C为智能指针增加了删除器概念删除器本质上是一个可调用对象如仿函数、lamda、函数不过shared_ptr是在构造时给删除器而unique_ptr是在类模板参数处给删除器。由于new[]使用频繁C将unique_ptr和shared_ptr都实现了特化这里的特化与前面讲的“shared_ptr的删除器要传给构造”要区分开是不同的方法unique_ptrDate[] up1(new Date[5]);shared_ptrDate[] sp1(new Date[5]);shared_ptr除了支持用指向资源的指针构造还支持make_shared用初始化资源对象的值直接构造我们可以大致将make_shared函数理解如下设计成可变参数模板是为了当类型如果是一个有较多成员变量的类时依然可用shared_ptr和unique_ptr都支持了operator bool的类型转换如果智能指针对象是一个空对象没有管理资源则返回false否则返回true意味着我们可以直接把智能指针对象给if判断是否为空shared_ptr和unique_ptr都得构造函数都使用explicit修饰防止普通指针隐式类型转换成智能指针对象普通指针隐式类型转换成智能指针对象看起来很方便但实际上很危险因为普通指针一旦被自动交给智能指针管理所有权关系可能变得非常不清楚模拟实现难点1、引用计数的类型是什么2、删除器加入到shared_ptr的构造函数后怎么让析构函数可以复用让删除器成为成员变量那么此成员变量是什么类型而且其默认删除器怎么设置下图只是简单模拟实现基本原理引用计数若是搭配weak_ptr实现起来会更复杂有兴趣的可以去查阅源码但是shared_ptr在这样的场景循环引用中会存在问题这样会出现内存泄漏问题sp1析构指向左侧链表结点的引用计数为1。sp2析构指向右侧链表结点的引用计数为1。左侧结点如果想释放需要右侧结点的_prev释放右侧结点的_prev若要释放需要右侧结点释放右侧结点若想释放需要左侧结点的_next释放左侧结点的_next若要释放需要左侧结点释放。哈哈是不是闭环了不倒翁。此时内存就会泄漏。C11为了解决此问题创造出了weak_ptrweak_ptr不支持RAll不会管理资源也不支持访问资源只支持绑定到shared_ptr绑定到shared_ptr时不增加shared_ptr的引|用计数此时就可以解决上述的循环引用问题为什么不管理资源呢它不会增加引用计数若它还指向资源如果资源已经释放了那么他去访问资源就是很危险的。weak_ptr有接口expired检查指向的资源是否过期资源是否释放use_count也可获取shared_ptr的引用计数weak_ptr想访问资源时可以调用lock返回一个管理资源的shared_ptr如果资源已经被释放返回的shared_ptr是一个空对象如果资源没有释放则通过返回的shared_ptr访问资源是安全的改造成这样循环引用的问题就解决了shared_ptr的还存在线程安全问题这里我还没学到等我学完了再来介绍