【Effective Modern C++】第四章 智能指针:19. 对于共享资源使用共享指针

定位与实现原理

  1. 功能 :管理共享所有权 的资源,兼具 "自动垃圾回收"(无需手动管理生命周期)和 "销毁可预测"(最后一个指向资源的 shared_ptr 销毁时,资源确定销毁)的优势,比纯垃圾回收语言的资源管理更通用、销毁时机更可控。

  2. 实现基础 :引用计数(跟踪指向资源的 shared_ptr 数量):

    • 拷贝 / 构造:递增引用计数;
    • 析构 / 赋值指向其他对象:递减引用计数;计数归 0 时销毁资源;
    • 移动更高效:移动 shared_ptr 无需修改引用计数(源指针置空),比拷贝快。
  3. 性能开销 (对比原始指针 /unique_ptr):

    • 大小:原始指针的 2 倍(存储 "指向资源的指针"+"指向引用计数的指针");
    • 额外成本:引用计数需动态分配(make_shared 可优化)、引用计数修改是原子操作(线程安全但比非原子操作慢)。

自定义删除器

差异:删除器类型不是 shared_ptr 类型的一部分(unique_ptr 的删除器是类型一部分);

  • 优势:不同删除器的同类型 shared_ptr(如 shared_ptr<Widget>)可放入同一容器、相互赋值、传入同一函数;
  • 大小不变:删除器存储在 "控制块" 中,而非 shared_ptr 对象本身,因此无论删除器是何类型,shared_ptr 始终是 2 个指针大小。

控制块

  1. 定义:每个被 shared_ptr 管理的资源对应一个控制块,除引用计数外,还存储自定义删除器 / 分配器等;

  2. 创建规则(错用会导致未定义行为):

    • make_shared、从 unique_ptr/ 原始指针构造:创建新控制块;
    • 从 shared_ptr/weak_ptr 构造:复用已有控制块(不新建);
  3. 致命风险:从同一原始指针 创建多个 shared_ptr → 生成多个控制块 → 资源被多次销毁;

    • 规避:① 直接传 new 的结果给 shared_ptr 构造函数(而非原始指针变量);② 用已有 shared_ptr 拷贝构造新对象(复用控制块)。

this 指针的安全使用(enable_shared_from_this)

  1. 问题:成员函数中直接传 this(原始指针)给 shared_ptr → 可能为同一对象创建新控制块(若外部已有指向该对象的 shared_ptr);
  2. 解决方案:让类继承 std::enable_shared_from_this<类名>(CRTP 模式),调用 shared_from_this() 替代 this(复用已有控制块);
  3. 前提:调用 shared_from_this() 前,已有指向该对象的 shared_ptr(否则行为未定义);最佳实践是类的构造函数私有化,通过工厂函数创建 shared_ptr 对象。

局限性与选型建议

  1. 不支持数组(C++17 前):API 为单个对象设计(无 operator[]),强行管理数组是糟糕设计,优先用 std::array/vector;
  2. 所有权不可变更:一旦资源由 shared_ptr 管理,即使引用计数为 1,也无法转为 unique_ptr;
  3. 选型原则:能独占则优先用 unique_ptr(更高效),unique_ptr 可无缝转为 shared_ptr;仅确需共享所有权时,才用 shared_ptr。

总结

  1. std::shared_ptr 是共享所有权资源的 "自动垃圾回收" 方案,核心靠引用计数 + 控制块实现,但有大小、原子操作等性能开销;
  2. 自定义删除器不影响 shared_ptr 类型,这是其比 unique_ptr 灵活的关键,但删除器存储在控制块中;
  3. 避坑点:禁止从同一原始指针创建多个 shared_ptr,成员函数中用 shared_from_this() 替代直接传 this;
  4. 优先用 unique_ptr 管理独占资源,仅共享时用 shared_ptr。

原著在线阅读地址

相关推荐
h39742 小时前
DirectShow过滤器开发-视频源过滤器
c++·windows·音视频·directshow
程序猿编码4 小时前
榨干 RTX5090 算力!Qwen3 专用单卡推理引擎,手写C++/CUDA 算子实现 MTP 推测解码
开发语言·c++·大模型推理·qwen3·多模态推理
西飘客5 小时前
vs 怎么根据dll 生成lib文件
c++·qt
程序员老陆6 小时前
深入理解 C++ thread_local:线程私有存储的正确打开方式
开发语言·c++·程序设计
半杯咖啡半行码9 小时前
C++ 编程与 STL 模板:从泛型编程到内存安全详解
开发语言·c++
xh didida10 小时前
Linux -- 基础IO
linux·服务器·开发语言·c++
垆边人似月.10 小时前
华为机试题 :两数之和
c++·算法·华为
个 人 练 习 生10 小时前
C++ string 类模拟实现:从底层理解字符串(上)
开发语言·c++·经验分享·学习·程序人生
垆边人似月.10 小时前
华为机试题 :有效的括号
c++·算法·华为
坤坤子吖10 小时前
C++智能指针:RAII、shared_ptr与内存泄漏
开发语言·c++·笔记·学习