【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

原著在线阅读地址

相关推荐
czxyvX2 小时前
016-二叉搜索树(C++实现)
开发语言·数据结构·c++
阿猿收手吧!2 小时前
【C++】volatile与线程安全:核心区别解析
java·c++·安全
Trouvaille ~2 小时前
【Linux】网络编程基础(三):Socket编程预备知识
linux·运维·服务器·网络·c++·socket·网络字节序
-dzk-2 小时前
【代码随想录】LC 707.设计链表
数据结构·c++·算法·链表
txinyu的博客3 小时前
解析muduo源码之 Buffer.h & Buffer.cc
c++
阿猿收手吧!3 小时前
【C++】异常处理:catch块执行后程序如何继续
服务器·网络·c++
代码游侠3 小时前
C语言核心概念复习(一)
c语言·开发语言·c++·笔记·学习
Once_day3 小时前
C++之《Effective C++》读书总结(3)
c语言·c++
蜕变的土豆3 小时前
grpc-通关速成
开发语言·c++