• 【C++进阶】:智能指针


    一.为什么需要智能指针

    主要两点原因:
    1.为了避免写代码时忘记delete导致的内存泄漏
    2.为了避免抛异常时跳过delete导致的内存泄漏。

    在这里插入图片描述

    1.new本身有可能出现异常,导致p1申请了空间但没直接跳过释放
    2.调用div时出现异常,直接抛出异常的同时也跳过了delete.

    二.智能指针的原理和使用

    1.RALL

    RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
    1.不需要显式地释放资源。
    2.采用这种方式,对象所需的资源在其生命期内始终保持有效。

    在这里插入图片描述

    上述的SmartPtr还不能将其称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可以通过->去访问所指空间中的内容,因此:AutoPtr模板类中还得需要将* 、->重载下,才可让其像指针一样去使用。

    2.auto_ptr

    C++98版本的库中就提供了auto_ptr的智能指针。下面演示的auto_ptr的使用及问题。
    auto_ptr的实现原理:管理权转移的思想,下面简化模拟实现了一份bit::auto_ptr来了解它的原
    理。

    在这里插入图片描述

    在这里插入图片描述

    可以发现auto_ptr有着严重的缺陷,所以目前很多公司都禁止使用它,对此我们也简单了解即可。

    3.unique_ptr

    C++11中开始提供更靠谱的unique_ptr

    unique_ptr的实现原理:简单粗暴的防拷贝,下面简化模拟实现了一份UniquePtr来了解它的原
    理。

    在这里插入图片描述

    4.shared_ptr

    很明显unique_ptr有时候满足不了需求,所以需要一个更强大的智能指针。

    shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源

    1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共
      享。
    2. 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减
      一。
    3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
    4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对
      象就成野指针了。

    在这里插入图片描述

    循环引用

    shared_ptr在大部分情况下都是可以正常使用的,但任有一些特殊情况导致内存泄漏:循环引用

    在这里插入图片描述

    在这里插入图片描述

    这一段代码没有打印任何数据,说明ListNode也并没有进行析构,这是为什么呢?

    循环引用分析:

    1. node1和node2两个智能指针对象指向两个节点,引用计数变成1,我们不需要手动
      delete。
    2. node1的_next指向node2,node2的_prev指向node1,引用计数变成2。
    3. node1和node2析构,引用计数减到1,但是_next还指向下一个节点且_prev还指向上
      一个节点。
    4. 也就是说_next析构了,node2就释放了。
    5. 也就是说_prev析构了,node1就释放了。
    6. 但是_next属于node的成员,node1释放了,_next才会析构,而node1由_prev管理,_prev
      属于node2成员,所以这就叫循环引用,谁也不会释放。

    在这里插入图片描述

    解决方案

    很简单,将shared_ptr换成弱指针weak_ptr即可。

    在这里插入图片描述

    在这里插入图片描述

    三.定制删除器

    上述我们对于析构函数仅仅只是简单的delete,如果是更复杂的情况呢?

    在这里插入图片描述

    毫无疑问会出现问题,析构不完全。为了解决这类问题库里引入了定制删除器。

    在这里插入图片描述

    实际上在外部写一个可调用对象即可(仿函数,lambda…)首先使用库里的。

    在这里插入图片描述

    模拟实现

    可以使用包装器实现。

    在这里插入图片描述

  • 相关阅读:
    【Python基础】if __name__ == ‘__main__‘:和assert函数
    Intellij IDEA快捷键大全汇总(二)
    数理天地杂志数理天地杂志社数理天地编辑部2022年第20期目录
    java连接zookeeper
    C++:哈希表的模拟实现
    记一次 .NET 某工厂无人车调度系统 线程爆高分析
    MQ - 39 Serverless : 基于MQ和Serverless设计事件驱动架构
    【性能测试】ChaosTesting(混沌测试)&ChaosBlade(混沌实验工具)(四)-k8s容器混沌实验
    分页文件pagefile.sys引出的疑问
    springboot - 2.7.3版本 - (八)ELK整合Kafka
  • 原文地址:https://blog.csdn.net/m0_73790767/article/details/133688999