【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

原著在线阅读地址

相关推荐
为何创造硅基生物43 分钟前
独占指针的创建std::make_unique 本身自带堆出现
c++
kyle~1 小时前
ROS 2 与 Isaac Sim 联合仿真(一)体系架构、环境选型与基础通信闭环
c++·机器人·nvidia·仿真·ros2
努力努力再努力wz1 小时前
【内存管理与高并发内存池系列】从 mmap 到 malloc:文件映射、匿名映射与 glibc 内存分配机制详解
linux·c语言·数据结构·数据库·c++·qt·链表
八解毒剂2 小时前
数据结构-平衡二叉树——对二叉搜索树的优化
数据结构·c++·算法
起床困难户5752 小时前
条款20:协助完成返回值优化
c++
啦啦啦啦啦zzzz2 小时前
算法总结(二分查找、双指针)
c++·算法
不负岁月无痕4 小时前
C++ 模板核心内容与高频面试题汇总
java·开发语言·c++
无限进步_4 小时前
从零实现一个迷你Shell——深入理解Linux命令行解释器
linux·运维·服务器·开发语言·c++·chrome
fpcc4 小时前
工具使用——CMake中的函数和宏
c++·cmake
乐观勇敢坚强的老彭5 小时前
C++信息学奥赛lesson1
java·开发语言·c++