• 用C++11 make_shared替代shared_ptr


    我们先看一下shared_ptr的成员结构

    shared_ptr 由继承_Ptr_base而来

    class shared_ptr : public _Ptr_base<_Ty>
    
    • 1

    _Ptr_base有两个成员,_Ptr用于指向管理的资源,_Rep用于指向引用计数对象

    template <class _Ty>
    class _Ptr_base {
    
    private:
        element_type* _Ptr{nullptr};
        _Ref_count_base* _Rep{nullptr};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    _Ref_count_base对象有两个成员,_Uses 表示有多少个shared_ptr指向资源,_Weaks表示有多少个weak_ptr指向资源

    // CLASS _Ref_count_base
    class __declspec(novtable) _Ref_count_base {
    private:
        _Atomic_counter_t _Uses  = 1;   // 多少个shared_ptr指向资源
        _Atomic_counter_t _Weaks = 1;   // 多少个weak_ptr观察资源
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    shared_ptr<int> sp1(new int(10));
    
    • 1

    在这里插入图片描述

    shared_ptr<int> sp2(sp1);  // 调用shared_ptr的拷贝构造,引用计数_Uses++,只存在一个引用计数对象
    weak_ptr<int> wp2(sp1);    // 调用weak_ptr的拷贝构造,引用计数_Weaks++,只存在一个引用计数对象
    
    • 1
    • 2

    调用智能指针的拷贝构造,会修改相应的引用计数,如果调用智能指针的构造函数,就会产生新的引用计数对象

    • int(10)这块资源释放:只要_Uses为0,立马释放
    • 引用计数对象空间释放:_Uses和_Weaks全都为0

    即这两块内存分两次new出来的,可以分开释放,weak_ptr的lock方法会返回shared_ptr,会使得_Uses++而_Weaks不变

    手动调用new缺陷: 如果 shared_ptr sp1(new int(10)) 这行代码中的new int(10) 执行成功了,而shared_ptr的构造函数执行失败了,就不会有引用计数的_Ref_count_base对象,就意味着不存在shared_ptr对象,就不会调用shared_ptr的析构函数,那我们new出来的堆区资源也就不会释放了

    shared_ptr<int> sp3 = make_shared<int>(10);
    
    • 1

    在这里插入图片描述
    用make_shared的原理如图,代码上不会再看见显式的new运算符,我们如果调用shared_ptr构造函数时,会手动new一次资源,shared_ptr的构造函数又会new一个引用计数的对象,如果两次new不能都成功,就会有资源泄露

    而make_shared把资源和引用计数的对象放在连续的空间中,就只需要new一次,解决了上面的问题。new失败没有资源泄露,new成功析构函数会释放资源

    make_shared优点: 申请空间效率高,防止了资源泄露

    make_shared缺点:

    1. 无法自定义删除器,默认析构函数是delete,无法管理打开的文件。如果想自定义删除器,还得使用第一个版本
    2. 分配的资源空间和引用计数对象的空间是连续的,是一次性申请的,也需要一次性释放,导致托管的资源延迟释放

    由于使用make_shared分配的资源空间和引用计数对象的空间是连续的,是一次性申请的,也需要一次性释放。所以对于make_shared new出来的内存,就算引用计数_Uses为0,而_Weaks不为0,无论是int(10)的空间还是引用计数对象空间都不能释放,因为_Weaks不为0,引用计数对象空间不能释放,整块资源都不能释放

    同样的,也需要用make_unique代替unique_ptr

  • 相关阅读:
    Redis发布订阅机制学习
    KV Cache
    数据结构线性表之双链表
    Gitee 图床被屏蔽后,我搭建了一个文件系统并封装成轮子开源
    (五)Linux 4G模块封装发送指令函数以及检测串口和SIM卡是否就绪
    log4j CVE-2021-44228 RCE漏洞复现
    哈工大李治军老师操作系统笔记【7】:多进程图像(Learning OS Concepts By Coding Them !)
    Linux 内核分析 rcu_sched self-detected stall on CPU
    c语言练习73:统计位数为偶数的数字
    三、lock类的编写与解析 —— TinyWebServer
  • 原文地址:https://blog.csdn.net/qq_42500831/article/details/126605465