【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

原著在线阅读地址

相关推荐
啦啦啦啦啦zzzz40 分钟前
算法:回溯算法
c++·算法·leetcode
Lzg_na1 小时前
订阅发布模块事例
c++
郝学胜-神的一滴1 小时前
[简化版 GAMES 104] 现代游戏引擎 02:拆解现代游戏引擎5+1层级架构,吃透引擎底层核心逻辑
c++·unity·架构·游戏引擎·图形渲染·unreal engine·系统设计
汉克老师1 小时前
2026年CSP-J初赛分数线-----预测
c++·csp-j·小学生·学c++编程
脱胎换骨-军哥1 小时前
C++数据库存储引擎内核开发:B+树索引与MVCC并发控制的完整实现
数据库·c++·b树
小保CPP2 小时前
OpenCV C++将多张图像合并为webp动图
c++·人工智能·opencv·计算机视觉
乐观勇敢坚强的老彭3 小时前
信奥C++一维数组笔记
开发语言·c++·笔记
码上有光3 小时前
异常和智能指针
java·大数据·c++·servlet·异常·智能指针
王维同学3 小时前
[原创][Windows C++]Explorer Shell 扩展、图标覆盖与 COM 服务器定位
c++·windows·注册表
code_pgf3 小时前
C++ 工程架构设计规范
c++·机器人