编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • C语言入门精通

  • Cpp入门到精通

    • README
    • 入门教程

    • 综合案例

    • 专栏博客

      • README
      • 进程地址空间布局
      • 对象内存布局原理
      • 引用与指针本质
      • this指针与成员函数
      • 虚函数表深度剖析
      • 多重继承内存模型
      • 内存对齐与缓存行
      • 内存分配器演进史
      • 五大值类别详解
      • 右值引用与移动语义
      • 完美转发与引用折叠
      • 类型推导三大规则
      • 类型转换与隐式构造
      • const与volatile真相
      • RTTI与dynamic_cast
      • 类型擦除技术原理
      • 模板实例化机制
      • 模板特化与偏特化
      • SFINAE与enable_if
      • 可变参数模板原理
      • constexpr编译期计算
      • Concepts深度剖析
      • 元编程模板技巧
      • Modules模块化设计
      • RAII的设计哲学
      • 对象构造与析构
      • 拷贝与移动控制
      • unique_ptr原理剖析
      • shared_ptr底层剖析
      • weak_ptr与this增强
        • 1. 案例引入
          • 1.1 回调里的 this 幽灵指针
          • 1.2 sharedfromthis 的重复初始化崩溃
          • 1.3 七个待解疑问
        • 2. 架构概览
          • 2.1 weak_ptr 对 this 的两层增强
          • 2.2 为何这么切
        • 3. this 裸指针的失效困局
          • 3.1 回调的四种时效模型
          • 3.2 裸 this 在异步世界的定时炸弹
          • 3.3 用 shared_ptr 包裹 this 的错误方式——完整原理
          • 3.4 weak_ptr 怎样终结悬垂
        • 4. enablesharedfrom_this 的 CRTP 设计
          • 4.1 源码级结构拆解
          • 4.2 sharedfromthis 的初始化流水线
          • 4.3 为什么必须 CRTP 而不是普通基类
          • 4.4 构造函数、析构函数里的 sharedfromthis
        • 5. weakfromthis 的 C++17 引入
          • 5.1 weakfromthis vs sharedfromthis 的语义区分
          • 5.2 典型使用场景:观察者与通知
          • 5.3 weakfromthis 的汇编开销
        • 6. 二级指针失效检测
          • 6.1 什么是二级指针失效
          • 6.2 weak_ptr 的 lock 是唯一的原子检测器
          • 6.3 与 raw this + flag 的对比——深入原理
          • 6.4 泛型观察者基类的实现
        • 7. Observer 模式的 weak_ptr 实现
          • 7.1 Subject-Observer 的经典范本
          • 7.2 通知时自动跳过已失效的观察者
          • 7.3 淘汰观察者的惰性清理
          • 7.4 与 signal/slot 库的性能对比
        • 8. 异步回调与生命周期边界
          • 8.1 异步投递的四层安全方案
          • 8.2 Timer/IO 回调的最佳实践
          • 8.3 线程池 + weak_ptr 的回调范本
        • 9. 边界陷阱与避坑指南
          • 9.1 不应该继承 enablesharedfrom_this 的场景
          • 9.2 多态删除与 enablesharedfrom_this
          • 9.3 静态函数中的 sharedfromthis
          • 9.4 与 unique_ptr 的 this 管理模式对比
        • 10. 综合案例串讲
          • 10.1 案例真相揭晓
          • 10.2 一次异步回调的完整安全链路
          • 10.3 设计哲学回扣
          • 10.4 速查表合集
      • 五种存储期管理
      • vector扩容真相
      • deque分段连续设计
      • list与forward_list
      • 关联容器红黑树
      • 哈希容器深度剖析
      • 迭代器五大类别
      • STL算法设计哲学
      • Allocator分配器机制
      • C++内存模型基石
      • 六大内存序详解
      • atomic原子操作原理
      • mutex与条件变量
      • thread与jthread机制
      • 异步编程future家族
      • 无锁数据结构设计
      • 协程coroutine原理
      • 翻译单元与预处理
      • 编译期符号生成
      • 链接器工作原理
      • ODR规则与陷阱
      • 动态库与符号可见性
      • C++ ABI兼容性
      • LTO与PGO优化
      • 异常机制底层原理
      • Ranges革命与管道
      • format与print体系
      • UB未定义行为图鉴
      • C++设计哲学回望
      • 写作模板
    • 开发技巧

  • Java入门精通

  • Go入门到精通

  • JavaScript入门

  • CodeX
  • Cpp入门到精通
  • 专栏博客
杨充
2026-06-06
目录

weak_ptr与this增强

# 30.weak_ptr与this增强

# 目录介绍

  • 1. 案例引入
    • 1.1 回调里的 this 幽灵指针
    • 1.2 shared_from_this 的重复初始化崩溃
    • 1.3 七个待解疑问
  • 2. 架构概览
    • 2.1 weak_ptr 对 this 的两层增强
    • 2.2 为何这么切
  • 3. this 裸指针的失效困局
    • 3.1 回调的四种时效模型
    • 3.2 裸 this 在异步世界的定时炸弹
    • 3.3 用 shared_ptr 包裹 this 的错误方式
    • 3.4 weak_ptr 怎样终结悬垂
  • 4. enable_shared_from_this 的 CRTP 设计
    • 4.1 源码级结构拆解
    • 4.2 shared_from_this 的初始化流水线
    • 4.3 为什么必须 CRTP 而不是普通基类
    • 4.4 构造函数、析构函数里的 shared_from_this
  • 5. weak_from_this 的 C++17 引入
    • 5.1 weak_from_this vs shared_from_this 的语义区分
    • 5.2 典型使用场景:观察者与通知
    • 5.3 weak_from_this 的汇编开销
  • 6. 二级指针失效检测
    • 6.1 什么是二级指针失效
    • 6.2 weak_ptr 的 lock 是唯一的原子检测器
    • 6.3 与 raw this + flag 的对比
    • 6.4 泛型观察者基类的实现
  • 7. Observer 模式的 weak_ptr 实现
    • 7.1 Subject-Observer 的经典范本
    • 7.2 通知时自动跳过已失效的观察者
    • 7.3 淘汰观察者的惰性清理
    • 7.4 与 signal/slot 库的性能对比
  • 8. 异步回调与生命周期边界
    • 8.1 异步投递的四层安全方案
    • 8.2 Timer/IO 回调的最佳实践
    • 8.3 线程池 + weak_ptr 的回调范本
  • 9. 边界陷阱与避坑指南
    • 9.1 不应该继承 enable_shared_from_this 的场景
    • 9.2 多态删除与 enable_shared_from_this
    • 9.3 静态函数中的 shared_from_this
    • 9.4 与 unique_ptr 的 this 管理模式对比
  • 10. 综合案例串讲
    • 10.1 案例真相揭晓
    • 10.2 一次异步回调的完整安全链路
    • 10.3 设计哲学回扣
    • 10.4 速查表合集

# 1. 案例引入

# 1.1 回调里的 this 幽灵指针

某即时通讯 SDK 的事件通知系统,用户上线后随机崩溃。崩溃栈总指在同一个位置——Session::on_message:

// ====== 事故代码 V1:裸 this 在异步回调中悬垂 ======

class Session {
    int fd_;
    std::string user_id_;
public:
    Session(int fd, std::string uid) : fd_(fd), user_id_(std::move(uid)) {}

    void start() {
        // ① 向 IO 线程注册回调——传递裸 this
        io_context.post([this] {
            read_loop();     // ← 如果 Session 在这之前被析构了?
        });
    }

    void read_loop() {
        char buf[4096];
        while (read(fd_, buf, sizeof(buf)) > 0) {
            handle_message(buf);
        }
    }

    ~Session() {
        close(fd_);
        io_context.cancel_all();  // ② 试图取消回调——但 IO 线程已经在执行 read_loop 了
    }
};

// ③ 用户正常退出 → ~Session() → close(fd) → 回调还在跑 → read 已关闭的 fd
//    或者:read_loop 里访问 user_id_ → 对象已析构 → 读已释放内存 → SIGSEGV
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

现象:用户关掉窗口 → Session 析构 → IO 线程调度到已在队列中的回调 → lambda 里的 this 已经指向了回收的内存 → user_id_ 读出来是 "訸訸\u07fd\x9c" → handle_message 里 std::string 的析构崩在 free 上。

直觉修复——析构函数里 cancel_all——但 cancel 是异步的:取消请求发出时回调可能已经在执行。裸 this 没有手段让回调知道「对象是否还活着」。

# 1.2 shared_from_this 的重复初始化崩溃

同一个代码库的 Connection 类,作者在构造函数里调了 shared_from_this——试图在构造期就把自己注册进连接管理器:

// ====== 事故代码 V2:构造期调用 shared_from_this ======
class Connection : public std::enable_shared_from_this<Connection> {
public:
    Connection(io_service& io) {
        auto self = shared_from_this();         // ❌ 抛 std::bad_weak_ptr
        connection_manager.register_conn(self); // 这一行永远不会执行
    }
};

auto conn = std::make_shared<Connection>(io);
// make_shared 内部:
//   ① 分配内存
//   ② Connection 的构造函数运行 → shared_from_this() → ❌
//   ③ shared_ptr 的构造函数才初始化 enable_shared_from_this 的 weak_ptr
// 步骤 ② 在步骤 ③ 之前——weak_this_ 还是空的!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

正确的做法必须把 shared_from_this 的调用推迟到构造函数完成之后——但一旦离开构造函数,就没有自然的「初始化后执行点」。这就是 enable_shared_from_this 的最深陷阱:你想要的执行点(构造后立刻)和安全的执行点(构造完成之后)之间有一道谁也无法逾越的鸿沟。

# 1.3 七个待解疑问

① 为什么回调里不能只传递裸 this? 四个时效模型有什么区别?              → 第 3 章
② enable_shared_from_this 的 CRTP 设计到底怎么工作? 为什么是 CRTP?    → 第 4 章
③ C++17 的 weak_from_this 和 shared_from_this 有什么区别? 怎么选?    → 第 5 章
④ 什么是二级指针失效? weak_ptr 如何检测对象的「还活着」状态?           → 第 6 章
⑤ Observer 模式用 weak_ptr 怎么实现? 和 shared_ptr 版有什么区别?      → 第 7 章
⑥ 异步回调(线程池/Timer/IO)的最佳生命周期安全方案是什么?              → 第 8 章
⑦ 有哪些场景不该用 enable_shared_from_this? 有什么边界陷阱?            → 第 9 / 第 10 章
1
2
3
4
5
6
7

# 2. 架构概览

# 2.1 weak_ptr 对 this 的两层增强

                    ┌──────────────────────────────────────┐
                    │    weak_ptr 对裸 this 的增强体系       │
                    └──────────────────┬───────────────────┘
                                       │
        ┌──────────────────────────────┼──────────────────────────────┐
        ▼                              ▼                              ▼
┌──────────────────┐        ┌──────────────────┐        ┌──────────────────┐
│ ① 安全的自我引用  │        │ ② 二级失效检测    │        │ ③ 观察者通知     │
│ enable_shared_   │        │ weak_ptr::lock   │        │ Observer 模式    │
│ from_this        │        │ 原子快照          │        │ 自动跳过失效     │
├──────────────────┤        ├──────────────────┤        ├──────────────────┤
│ shared_from_this │        │ expired()→true    │        │ notify 时 lock    │
│ weak_from_this   │        │ → 不再访问        │        │ 成功才调用        │
│ (C++17)          │        │ 无竞态窗口        │        │ 失败自动移除      │
└──────────────────┘        └──────────────────┘        └──────────────────┘
        │                              │                              │
        └──────────────────────────────┼──────────────────────────────┘
                                       ▼
                    ┌──────────────────────────────────────┐
                    │  从「信任裸指针」到「运行期验证存活」   │
                    │  零假阴性——只要 lock 成功,对象一定活着 │
                    └──────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 2.2 为何这么切

疑惑:为什么需要 enable_shared_from_this + weak_from_this 这套机制?直接把 shared_ptr<this> 存起来不行吗?

论证:

  1. this 不携带所有权信息——一个裸 this 指针不知道它是被 unique_ptr、shared_ptr、还是在栈上管理的。enable_shared_from_this 通过 CRTP 把 weak_ptr<Derived> 嵌入对象内部——对象本身不拥有 shared_ptr,但保留了一个「能找到已存在的 shared_ptr 控制块」的路径。
  2. 二级指针失效的本质——回调持有的是 this 指针。当对象析构后,this 变成悬空指针——没有运行时机制能检测「这个地址上的对象还在不在」。weak_ptr::lock() 是唯一能原子地检测对象存活状态 + 获取安全引用的机制。
  3. Observer 模式需要「不拥有、但能检测」的语义——Subject 不应该阻止 Observer 被释放(如果用 shared_ptr<Observer>,Observer 永远释放不掉)。weak_ptr 正好提供了「我观察你、我不拥有你、你如果死了我能知道」的三件套。
  4. 反向验证:Rust 的 Weak<T>(对应 C++ 的 weak_ptr<T>)和 Java 的 WeakReference<T> 也提供了类似机制——但 Java 的版本依赖 GC 的非确定性回收。C++ 的 weak_ptr 是唯一确定性地在对象析构瞬间就标记 expired() = true 的跨语言方案。

结论:weak_ptr 对 this 的增强不是语法糖——是在异步世界保护对象生命周期的最底层的、唯一编译期+运行期双保险的安全网。 裸 this = 信任、shared_ptr<this> = 强占有(阻止释放)、weak_ptr<this> = 观察+原子快照——三种所有权的三种语义。


# 3. this 裸指针的失效困局

# 3.1 回调的四种时效模型

任何传递回调的系统都必须面对一个核心问题:回调执行时,捕获的对象还在吗?

时效模型 ①:同步调用(Always Safe)
  obj.method()  → 直接在调用者的栈帧里执行
  → this 一定活着——因为调用者还在执行

时效模型 ②:异步-先于析构(Usually Safe)
  event_loop.post([this]{ ... });
  obj 的生命周期由外层 shared_ptr 保证
  → 只要外层 shared_ptr 在回调执行前没释放 → 安全
  → 但没有任何编译器/运行时机制保证这一点

时效模型 ③:异步-可能后于析构(Unsafe)
  event_loop.post([this]{ ... });
  obj 可能在任何时刻被释放(用户操作、网络超时、错误处理)
  → this 在回调执行时可能已经是悬空指针
  → ⚠️ 当前代码的默认模型

时效模型 ④:异步-安全锁定(Safe)
  event_loop.post([weak = weak_from_this()]{
      if (auto self = weak.lock()) { self->do_work(); }
  });
  → 回调执行时原子检查对象存活
  → ✅ 要么安全执行、要么安全跳过
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 3.2 裸 this 在异步世界的定时炸弹

案例 1.1 的根因展开——为什么 ~Session() 里的 cancel_all() 不能保证安全。

疑惑:cancel_all() 不是应该阻止后续回调执行吗?为什么还能崩?

论证——时序重构:

                    ┌─── 用户线程 ───┐        ┌─── IO 线程 ───┐
时间 →              │                │        │                │
T0      session.reset() 调用        │        │                │
T1      │   shared_ptr&lt;Session>      │        │                │
        │   最后一个引用释放          │        │                │
T2      │   ~Session() 开始          │        │  [this] lambda │
        │   ├─ close(fd_)            │        │  已在队列中     │
T3      │   ├─ cancel_all()          │        │  ← 调度 lambda │
        │   │  (仅标记"后续不投递")   │        │   read_loop()  │
T4      │   ├─ 析构 user_id_         │        │  read(fd_,...) │
        │   │  → 释放堆内存           │        │  → fd 已关闭   │
T5      │   ~Session() 完成          │        │  → EBADF       │
        │   → 整块内存还分配器        │        │  handle_msg()  │
T6      │                            │        │  → 访问        │
        │                            │        │   this->       │
        │                            │        │   user_id_     │
        │                            │        │   ← 垃圾值!    │
        │                            │        │   SIGSEGV      │
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

关键洞察:cancel_all() 和回调执行之间存在不可消除的竞态窗口。cancel_all() 只能阻止尚未入队的回调;对于已在 IO 线程执行队列中的回调,它完全无能为力。这不是 bug——这是异步系统的物理必然。

在分析这个时序时,最核心的理解是:cancel_all() 只改了逻辑状态("请不要投递新的回调"),但没有等待已经投递的回调执行完毕。要等待执行完毕,你需要的是 await 语义(如 cancel_all_and_wait()),但这在析构函数里会死锁——析构函数占着锁,等待的回调可能也需要锁。

clock 级量化:从 cancel_all() 返回到 ~Session() 完成,这中间通常只有几百纳秒。如果 IO 线程恰在此时刻把 lambda 出队——它访问的 this 在这几百纳秒之后就是悬空的。"恰在此时刻"的概率虽然不高(1/2000),但在每秒几十万连接的系统中,每天触发几十次。

# 3.3 用 shared_ptr 包裹 this 的错误方式——完整原理

一个自然的但错误的修复——直接构造 shared_ptr<this>:

class Session {
public:
    void start() {
        auto self = std::shared_ptr<Session>(this);   // ❌ 双重控制块!
        io_context.post([self] { self->read_loop(); });
    }
};

auto session = std::make_shared<Session>();
session->start();   // session.use_count() = 1(只有外部的 shared_ptr)
                    // start 内部又创建了一个独立控制块的 shared_ptr
                    // → 对象被两个控制块同时管理 → double free
1
2
3
4
5
6
7
8
9
10
11
12

为什么不能:shared_ptr<T>(this) 会创建一个全新的控制块——它不知道「这个对象已经被另一个 shared_ptr 管理」。两个控制块各自计数、第一个归零的析构对象、第二个归零的也析构同一个对象 → double free。

唯一的正确答案——enable_shared_from_this + shared_from_this()——找到已经存在的那个控制块,而不是创建新的。

# 3.4 weak_ptr 怎样终结悬垂

class Session : public std::enable_shared_from_this<Session> {
    int fd_;
public:
    void start() {
        auto weak_self = weak_from_this();      // ① 从已存在的控制块获取 weak_ptr
        io_context.post([weak_self] {
            if (auto self = weak_self.lock()) { // ② 原子检查+获取 shared_ptr
                self->read_loop();              // ③ self 保证在作用域内存活
            }
            // ④ 如果 lock 失败 → Session 已析构 → 安全跳过
        });
    }
};

auto session = std::make_shared<Session>();
session->start();
session.reset();    // ⑤ Session 析构 → fd close + ~Session 完成
// 如果 IO 线程的回调在这之后执行:
//   weak_self.lock() → 返回空 → 跳过 read_loop ✅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

三行代码(①→②→③→④),把从「信任」切换到「验证」。


# 4. enable_shared_from_this 的 CRTP 设计

# 4.1 源码级结构拆解

// libstdc++ 简化版——注释逐行解释设计意图
template <typename T>
class enable_shared_from_this {
    mutable weak_ptr<T> weak_this_;  // ① mutable —— 允许 const 成员函数调用
                                     //    shared_from_this() const
                                     //    weak_from_this() const

protected:
    constexpr enable_shared_from_this() noexcept = default;
    // ② 保护构造——只能通过派生类构造
    //    constexpr + noexcept —— 零开销(编译期可常量求值)

    enable_shared_from_this(const enable_shared_from_this&) noexcept {}
    // ③ 拷贝构造——不拷贝 weak_this_
    //    因为拷贝后的对象属于「另一个 shared_ptr 的簇」,
    //    不能共享原来的控制块

    enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept {
        return *this;  // ④ 拷贝赋值——同样不碰 weak_this_
    }

    ~enable_shared_from_this() = default;  // ⑤ 非虚析构——不增加 vptr

public:
    shared_ptr<T> shared_from_this() {
        return shared_ptr<T>(weak_this_);
        // ⑥ 从 weak_ptr 构造 shared_ptr——增加 strong_count
        //    如果 weak_this_ 过期 → 抛 std::bad_weak_ptr
    }

    shared_ptr<const T> shared_from_this() const {
        return shared_ptr<const T>(weak_this_);
    }

    weak_ptr<T> weak_from_this() noexcept {       // ⑦ C++17 新增
        return weak_this_;
    }

    weak_ptr<const T> weak_from_this() const noexcept {
        return weak_this_;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

关键设计点:

① mutable weak_ptr<T>——为什么要 mutable?

class Session : public enable_shared_from_this<Session> {
public:
    void do_work() const {       // ← const 成员函数
        auto self = shared_from_this();  // 需要修改基类的 weak_this_
        // 基类的 shared_ptr<Session>(weak_this_) 内部会 lock weak_this_
        // lock 涉及修改控制块的 strong_count——需要非 const 访问
        // mutable 允计 const 成员函数通过 const *this 修改 weak_this_
    }
};
1
2
3
4
5
6
7
8
9

②③④ 拷贝构造 / 赋值必须特化——默认的成员拷贝会把 weak_this_ 也拷过去,导致拷贝出的对象也指向原始对象的控制块。必须覆写、不拷。

shared_ptr 如何检测 enable_shared_from_this 基类——编译期+运行期双重机制:

// shared_ptr 的构造函数内部——简化版
template <typename T>
shared_ptr<T>::shared_ptr(T* p) {
    // ① 编译期检测:p 的类型是否派生自 enable_shared_from_this<T>?
    if constexpr (std::is_base_of_v<enable_shared_from_this<T>, T>) {
        // ② 运行期:调用基类的隐藏成员设置 weak_this_
        if (auto* esft = static_cast<enable_shared_from_this<T>*>(p)) {
            esft->__enable_shared_from_this(this);
            //     └─ 这个函数将 *this(正在构造的 shared_ptr)登记到基类的 weak_ptr
        }
    }
    // ③ 完成 shared_ptr 的正常构造(设置 ptr_ 和 ctrl_)
}
1
2
3
4
5
6
7
8
9
10
11
12
13

隐藏的 __enable_shared_from_this 成员(libstdc++ 内部):

template <typename T>
class enable_shared_from_this {
    mutable weak_ptr<T> weak_this_;

    // ⚠️ 这是「秘密」接口——不在公开文档中,但 libstdc++/libc++ 都实现了
    void __enable_shared_from_this(const shared_ptr<T>& sp) const noexcept {
        if (weak_this_.expired()) {    // ③ 只初始化一次——避免覆盖
            weak_this_ = sp;           // ④ 赋值——不是构造!保留已有的控制块引用
        }
    }

    template <typename U>
    friend class shared_ptr;           // ⑤ 只有 shared_ptr 能调用这个私有成员
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

为什么需要 if (weak_this_.expired()) 保护:防止以下代码导致 weak_this_ 被覆盖:

auto sp1 = std::make_shared<Session>();  // ① weak_this_ = sp1 控制块
std::shared_ptr<Session> sp2(sp1.get()); // ② sp2 有新的控制块!
// ③ 如果 sp2 的构造函数把 weak_this_ 改成 sp2 的控制块:
//    shared_from_this() → 返回 sp2 的控制块 → use_count = 1
//    sp1 析构 → 另一个控制块的 use_count → 0 → 析构对象
//    但 sp2 还活着并持有悬空指针!
1
2
3
4
5
6

这个保护不能防御从裸 new 创建两个独立 shared_ptr 的场景(因为两个控制块 expired() 都为 true)——这正是第 1.2 节的案例。

libc++ vs libstdc++ 的实现差异:

细节 libstdc++ (GCC) libc++ (Clang)
检测方式 __enable_shared_from_this 虚函数类层次 std::is_base_of + __enable 非虚
虚表影响 多一个 vtable 槽位 (+8B) 零 vtable 增加(用 SFINAE 重载)
多态兼容 ✅ 更容易处理多继承中的 esft ⚠️ 多继承需显式 cast
编译时间 稍慢(虚函数机制) 稍快(编译期 SFINAE)

# 4.2 shared_from_this 的初始化流水线

auto sp = std::make_shared&lt;Session>("user_42");

阶段 1:make_shared 内部分配
  void* mem = operator new(sizeof(control_block) + sizeof(Session))

阶段 2:构造对象
  ::new (session_addr) Session("user_42")
    → enable_shared_from_this&lt;Session>::enable_shared_from_this()
      → weak_this_ 默认构造——expired() = true ← 此时还不可用!
    → Session::Session() 执行
      → ❌ 不能在这里调 shared_from_this() → throw bad_weak_ptr

阶段 3:构造 shared_ptr
  shared_ptr&lt;Session> sp(session_addr, control_block_addr)
    → shared_ptr 构造函数检测到 Session 有 enable_shared_from_this 基类
    → 调用 enable_shared_from_this 的「秘密」成员函数 __enable_shared_from_this()
    → 设置 weak_this_ = *this  ← 现在 weak_this_ 有效了!

阶段 4:返回 shared_ptr
  sp 返回到调用方 → 此后任何成员函数都能安全调用 shared_from_this()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

为什么阶段 2 到阶段 3 之间有「不可用的窗口」:因为 shared_ptr 的构造函数必须在对象完全构造之后才能初始化 weak_this_——否则 weak_this_ 指向的是一个「还在构造中」的对象(不完整)。

# 4.3 为什么必须 CRTP 而不是普通基类

疑惑:为什么不设计成普通基类 enable_shared_from_this(不带模板参数)?所有类都继承它就行?

论证:

// ❌ 如果是不带模板的普通基类——
class enable_shared_from_this_base {
    weak_ptr<void> weak_this_;   // 擦除类型——存储 void*
};

// 使用方:
auto sp = shared_from_this_base();  // 返回 shared_ptr<void> —— 丢失了类型信息!
sp->method();                        // ❌ void* 不能调方法
1
2
3
4
5
6
7
8

CRTP 让 enable_shared_from_this<T> 的 T 在编译期就被替换为具体类型——shared_from_this() 返回 shared_ptr<Session> 而不是 shared_ptr<void>。唯一的多态替代方案是用虚函数做类型恢复——但那样 sizeof 至少 +8(vptr),且运行时开销。CRTP 的 zero-cost 体现在:不需要任何虚函数。

# 4.4 构造函数、析构函数里的 shared_from_this

调用位置 shared_from_this weak_from_this 说明
构造函数内 ❌ 抛出 bad_weak_ptr ✅ 可调用但 expired()=true weak_this_ 尚未初始化
构造完成后(成员函数) ✅ ✅ 正常使用
析构函数内 ⚠️ 标准不详——可能抛异常 ✅ expired()=false 对象在析构中,但不建议在析构里继续传播 shared_ptr

为什么析构函数里 shared_from_this 不安全:在 ~Derived() 执行时,派生类的部分已经被析构了。如果此时 shared_from_this() 返回的 shared_ptr 被传递到另一个线程——那个线程访问到的对象是「半毁」的。


# 5. weak_from_this 的 C++17 引入

# 5.1 weak_from_this vs shared_from_this 的语义区分

接口 返回 增加 strong_count? 失败行为 场景
shared_from_this() shared_ptr<T> ✅ 抛 bad_weak_ptr 调用方必须获得对象所有权
weak_from_this() weak_ptr<T> ❌ 返回空 weak_ptr 调用方只想「观察」

为什么 C++11 没有 weak_from_this:C++11 的 enable_shared_from_this 只有 shared_from_this。但实践中发现很多场景只需要 weak_ptr——如:

// C++11 的变通方法
auto weak = std::weak_ptr<Session>(shared_from_this());
// 但 shared_from_this() 可能抛异常!而且无谓地增加了 strong_count

// C++17 的简洁版
auto weak = weak_from_this();   // 绝不抛异常、不增 strong_count
1
2
3
4
5
6

# 5.2 典型使用场景:观察者与通知

class Subject : public std::enable_shared_from_this<Subject> {
    std::vector<std::weak_ptr<Observer>> observers_;
public:
    void register_observer(std::shared_ptr<Observer> obs) {
        observers_.push_back(obs);  // shared_ptr → weak_ptr 隐式转换
    }

    void notify_all() {
        auto self = weak_from_this();  // ① 只观察——不增加自己的引用计数
        for (auto it = observers_.begin(); it != observers_.end(); ) {
            if (auto obs = it->lock()) {
                obs->on_event(self);    // ② 传递 weak_ptr——Observer 不拥有 Subject
                ++it;
            } else {
                it = observers_.erase(it);  // ③ 惰性清理已失效的观察者
            }
        }
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

为什么传 weak_ptr 而不是 shared_ptr:如果 Subject 传 shared_ptr<Subject>(this) 给 Observer——Observer 存储这个 shared_ptr → Subject 永远释放不掉(循环引用)。weak_ptr 保证 Subject 不受 Observer 的生命周期束缚。

# 5.3 weak_from_this 的汇编开销

auto weak = obj.weak_from_this();  // Session 对象
1

汇编(GCC 13.2 -O2):

; weak_from_this() —— 仅拷贝 weak_this_ 的两个指针
mov   rax, [rdi+offset]        ; 读 weak_this_ 的控制块指针
mov   [rsi], rax               ; 存到 weak 的 ctrl_
mov   rax, [rdi+offset+8]      ; 读 weak_this_ 的对象指针
mov   [rsi+8], rax             ; 存到 weak 的 ptr_
; 4 条 mov——零原子操作(weak_this_ 本身在构造时已经设置好)
1
2
3
4
5
6

和 shared_from_this 对比:

; shared_from_this() —— 多了原子增
mov   rax, [rdi+offset]        ; 读控制块指针
mov   [rsi], ...               ; 存对象指针
mov   [rsi+8], rax             ; 存控制块指针
lock  add DWORD [rax], 1       ; ← 多了这条——原子增 strong_count
1
2
3
4
5

# 6. 二级指针失效检测

# 6.1 什么是二级指针失效

一级指针失效:T* 指向的对象被析构 → 该地址上的内存可能已被回收或复用 → 访问 = UB。

二级指针失效:回调持有的不是直接指向对象的指针,而是指向控制块的指针——通过控制块来间接探测对象是否存活:

一级指针(裸 this):
  回调 → this → [Session 对象]
  对象释放后 → this → [已回收/复用内存] → UB

二级指针(weak_ptr):
  回调 → weak_ptr → [控制块] → strong_count?
                  ├─ > 0 → 对象还活着 → lock 成功
                  └─ = 0 → 对象已析构 → lock 失败
  控制块独立于对象存活——对象析构不影响控制块的有效性
1
2
3
4
5
6
7
8
9

# 6.2 weak_ptr 的 lock 是唯一的原子检测器

第 29 篇 §4.4 详细拆解了 lock() 的 CAS 硬件语义。这里强调它在「二级指针检测」角色上的不可替代性:

// weak_ptr::lock() 是唯一能同时做到以下三点的操作:
// ① 一个原子操作内检查对象是否活着
// ② 如果活着——获取一个安全的 shared_ptr(保证对象在后续使用中不被释放)
// ③ 如果已死——安全地返回空(不会误判为「还活着」)

// ❌ 任何替代方案都有竞态窗口:
bool is_alive = (obj != nullptr);  // 读 obj——但 obj 可能在下一条指令被释放
if (is_alive) { obj->work(); }      // UB
1
2
3
4
5
6
7
8

# 6.3 与 raw this + flag 的对比——深入原理

一种常见的「手动检测」方案——在对象里放一个 bool alive_:

class Session {
    std::atomic<bool> alive_{true};
public:
    ~Session() { alive_ = false; }  // ← 标志失效——但内存还在
    void start() {
        io_context.post([this] {
            if (alive_) { read_loop(); }
        });
    }
};
1
2
3
4
5
6
7
8
9
10

疑惑:alive_ = false 在线程间是原子的——为什么还是不安全?

论证——三级失效链:

内存的生命周期 ≠ 标志位的生命周期

时刻 T1:~Session() → alive_ = false
         → Session 的内存仍然有效(析构还没完成)

时刻 T2:~Session() → 析构成员 → 内存开始释放
         → alive_ 的标志位作为成员被析构——标志本身已不可靠!

时刻 T3:operator delete → 内存还给分配器
         → 新的 Widget 对象覆盖了这块内存
         → alive_ 的位置现在存的是 Widget 的某个字段

时刻 T4:IO 线程执行 lambda
         → 读到 alive_ 「为 true」(被 Widget 的字段覆盖)
         → 通过 alive_ 检查 → 使用 this → SIGSEGV
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

根本问题:标志位和对象共享同一块内存——对象一死,标志位也死了。你需要的是一个不受对象析构影响的独立标记——这就是控制块的本质。 控制块在堆上独立分配,在 strong_count=0 时对象被析构、但控制块继续存活——因为可能还有 weak_ptr 引用它。

扩展对比——五维安全性评估:

方案 竞态窗口 假阳性率 假阴性率 内存安全 额外存储
裸 this ∞(无检测) — — ❌ 0
alive_ atomic flag 有 高(复用) 低 ❌ +1 byte
alive_ + 独立堆分配 有 低 低 ⚠️ +8 byte (ptr)
weak_ptr::lock() 零 零 零 ✅ +16 byte

结论:只有 weak_ptr 的控制块独立分配机制 + lock() 的 CAS 原子操作,能同时消灭假阳性、假阴性和竞态窗口。 其他方案都在其中至少一个维度上有缺口。

# 6.4 泛型观察者基类的实现

// 任何需要「安全自我引用」的类都可以继承这个
class SafeSelfReference : public std::enable_shared_from_this<SafeSelfReference> {
public:
    template <typename F>
    auto post_to(io_context& ctx, F&& callback) {
        auto weak = weak_from_this();
        ctx.post([weak, cb = std::forward<F>(callback)] {
            if (auto self = weak.lock()) {
                cb(*self);
            }
        });
    }
};

// 使用:
class Session : public SafeSelfReference {
    void start() {
        post_to(ctx, [](Session& self) { self.read_loop(); });
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 7. Observer 模式的 weak_ptr 实现

# 7.1 Subject-Observer 的经典范本

class Observer : public std::enable_shared_from_this<Observer> {
public:
    virtual ~Observer() = default;
    virtual void on_event() = 0;
};

class Subject {
    std::vector<std::weak_ptr<Observer>> observers_;  // ← weak_ptr——不拥有
    std::mutex mtx_;
public:
    void attach(std::shared_ptr<Observer> obs) {
        std::lock_guard g(mtx_);
        observers_.push_back(obs);
    }

    void detach(std::shared_ptr<Observer> obs) {
        std::lock_guard g(mtx_);
        // 惰性删除——不主动遍历移除,在 notify 时清理
    }

    void notify() {
        std::lock_guard g(mtx_);
        for (auto it = observers_.begin(); it != observers_.end(); ) {
            if (auto obs = it->lock()) {
                obs->on_event();
                ++it;
            } else {
                it = observers_.erase(it);  // ② 惰性清理
            }
        }
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 7.2 通知时自动跳过已失效的观察者

void Subject::notify() {
    for (auto it = observers_.begin(); it != observers_.end(); ) {
        if (auto obs = it->lock()) {
            obs->on_event();    // Observer 还活着 → 通知
            ++it;
        } else {
            it = observers_.erase(it);  // Observer 已死 → 移除
        }
    }
}
1
2
3
4
5
6
7
8
9
10

注意:notify 里的 ++it / erase(it) 是安全的——因为 lock() 返回的 shared_ptr 在 on_event() 调用期间保持 Observer 存活。Observer 不会在「判断存活」和「调用 on_event」之间被释放。

# 7.3 淘汰观察者的惰性清理

每次 notify 时遍历整个 observers_ 列表,用 lock() 判断每个 Observer 是否还活着。已失效的擦除。时间复杂度 O(N),N = Observer 数量。不需要 Observer 主动 detach——它的析构自动让 weak_ptr 过期。

# 7.4 与 signal/slot 库的性能对比

方案 自动清理 线程安全 额外开销
weak_ptr Observer ✅ (notify 时惰性清理) 需要 mutex 保护列表 +16 字节/observer
boost::signals2 ✅ (自动 track) ✅ 内置 +较大
Qt signal/slot ❌ 手动 disconnect 取决于 QThread QObject 不小
裸指针列表 ❌ 手动 ❌ 仅 8 字节/observer

# 8. 异步回调与生命周期边界

# 8.1 异步投递的四层安全方案

四层安全级别——从最危险到最安全:

第 1 层:裸 this + 祈祷
  io.post([this]{ do_work(); });
  → ❌ 无法检测对象是否存活

第 2 层:shared_ptr&lt;this> + 永不让它释放
  auto self = std::shared_ptr&lt;Session>(this);     // ❌ 双控制块
  io.post([self]{ self->do_work(); });
  → ❌ 要么 double-free、要么对象永不释放(循环引用)

第 3 层:enable_shared_from_this + shared_ptr capture
  auto self = shared_from_this();
  io.post([self]{ self->do_work(); });
  → ✅ 安全——但回调持有 shared_ptr,对象在所有回调完成前不释放

第 4 层:enable_shared_from_this + weak_ptr capture(最佳)
  auto weak = weak_from_this();
  io.post([weak]{
      if (auto self = weak.lock()) { self->do_work(); }
  });
  → ✅ 安全——回调不阻止对象释放;回调执行时自动检查存活
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 8.2 Timer/IO 回调的最佳实践

class Connection : public std::enable_shared_from_this<Connection> {
    asio::steady_timer timer_;
    void on_timeout() { /* ... */ }

public:
    void start_heartbeat() {
        auto weak = weak_from_this();
        timer_.expires_after(30s);
        timer_.async_wait([weak](std::error_code ec) {
            if (ec) return;                          // 被取消
            if (auto self = weak.lock()) {
                self->on_timeout();
                self->start_heartbeat();             // 递归设置下一次
            }
        });
    }
};
// 当 Connection 析构 → timer 取消 → ec 非零 → 回调返回
// 当 Connection 析构 → 但 cancel 异步——回调可能仍然触发
//   → weak.lock() 返回空 → 安全跳过
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 8.3 线程池 + weak_ptr 的回调范本

template <typename F>
void thread_pool_post(std::weak_ptr<Session> weak, F&& callback) {
    pool_.post([weak, cb = std::forward<F>(callback)] {
        if (auto self = weak.lock()) {
            cb(*self);
        }
    });
}

// 使用:
thread_pool_post(session->weak_from_this(), [](Session& s) {
    s.process();
});
1
2
3
4
5
6
7
8
9
10
11
12
13

# 9. 边界陷阱与避坑指南

# 9.1 不应该继承 enable_shared_from_this 的场景

场景 是否需要 原因
栈上对象 / unique_ptr 管理 ❌ 没有 shared_ptr 控制块——shared_from_this 必定抛异常
所有回调都同步执行 ❌ 不需要弱引用检测——this 始终有效
不需要自我引用的类 ❌ 增加 16 字节(weak_ptr)+ 概念负担
共享所有权 + 需要自我引用 ✅ 唯一真正需要的场景

# 9.2 多态删除与 enable_shared_from_this

struct Base : std::enable_shared_from_this<Base> {
    virtual ~Base() = default;
};
struct Derived : Base {
    void use() {
        auto self = shared_from_this();  // 返回 shared_ptr<Base> ——不是 Derived!
        // 如果调用方需要 shared_ptr<Derived>——必须 static_pointer_cast
    }
};
1
2
3
4
5
6
7
8
9

注意:shared_from_this() 返回的是 shared_ptr<Base>(基类类型),不是 shared_ptr<Derived>。如果需要派生类类型,必须:

auto self = std::static_pointer_cast<Derived>(shared_from_this());
1

# 9.3 静态函数中的 shared_from_this

class Factory {
public:
    static auto create() {
        auto p = std::make_shared<Factory>();
        p->init();
        return p;
    }
private:
    void init() {
        auto self = shared_from_this();  // ✅ 可以——p 是 shared_ptr 管理的
    }
};
1
2
3
4
5
6
7
8
9
10
11
12

静态函数本身不能调用 shared_from_this()——因为没有 this。但通过 shared_ptr 对象间接调用成员函数时可以。

# 9.4 与 unique_ptr 的 this 管理模式对比

维度 unique_ptr 管理 shared_ptr + enable_shared_from_this
自我引用 裸 this(危险) weak_from_this(安全)
回调安全 需要手动 flag weak_ptr::lock 原子检测
sizeof 增加 0 +16(weak_ptr)
适用场景 明确单个所有者 共享所有权+异步回调

# 10. 综合案例串讲

# 10.1 案例真相揭晓

# 疑问 答案
① 回调为什么不能传裸 this? 第 3 章:异步世界中,回调执行时对象可能已析构——裸 this 无法检测
② enable_shared_from_this 怎么工作? 第 4 章:基类藏 weak_ptr,shared_ptr 构造时初始化——CRTP 消去虚函数
③ weak_from_this vs shared_from_this? 第 5 章:前者不增加 strong_count、不抛异常——用于「观察」而非「持有」
④ 什么是二级指针失效? 第 6 章:通过控制块间接探测对象存活——控制块独立于对象存活
⑤ Observer 模式怎么实现? 第 7 章:weak_ptr 存观察者列表,notify 时 lock 检测+惰性清理
⑥ 异步回调的最佳方案? 第 8 章:四层安全级别——第 4 层 weak_ptr capture 最优
⑦ 不该用 enable_shared_from_this 的场景? 第 9 章:非 shared_ptr 管理/纯同步/不需要自我引用

案例①修复(this 幽灵):

class Session : public std::enable_shared_from_this<Session> {
public:
    void start() {
        auto weak = weak_from_this();
        io_context.post([weak] {
            if (auto self = weak.lock()) { self->read_loop(); }
        });
    }
};
// 三行修复——零泄露、零悬垂、零 double-free
1
2
3
4
5
6
7
8
9
10

案例②修复(构造期 shared_from_this):把 shared_from_this 调用移到构造后——用工厂函数:

static auto create(io_service& io) {
    auto conn = std::make_shared<Connection>(io);
    conn->init();   // 在这里调 shared_from_this——安全
    return conn;
}
1
2
3
4
5

# 10.2 一次异步回调的完整安全链路

auto session = std::make_shared&lt;Session>("user_42");
session->start();       // ← 投递回调
session.reset();        // ← 释放引用(对象析构)

═══════ 构造期 ═══════
  ① make_shared → new 对象 + 控制块
  ② shared_ptr 构造 → enable_shared_from_this::weak_this_ 被初始化
  ③ start() → weak_from_this() → 拷贝两个指针 → 包裹进 lambda → post 到 IO 队列

═══════ 析构期 ═══════
  ④ session.reset() → strong_count → 0
  ⑤ ~Session() 执行 → close fd + 清理成员
  ⑥ 控制块中 strong_count=0, weak_count>0(IO 队列的 lambda 还持有 weak_ptr)

═══════ 回调执行期 ═══════
  ⑦ IO 线程调度 lambda
  ⑧ weak.lock() → 读控制块 → strong_count=0 → 返回空 shared_ptr
  ⑨ if 失败 → 跳过 read_loop → lambda 析构 → weak_count-- → 控制块释放
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 10.3 设计哲学回扣

哲学 1:信任是漏洞——验证是安全

裸 this 在所有异步系统中都是定时炸弹——因为「对象已经析构」这件事是一个异步事件。weak_ptr 把「信任」换成「原子验证」——在回调执行的每一时刻,先验证再访问。 这个哲学超越了 C++:Rust 的 Weak<T>、Java 的 WeakReference、Swift 的 weak 引用——所有现代语言都在走向同一个方向:不拥有的引用必须可验证。C++ 的版本是确定性的、零假阳性的、无 GC 依赖的——这是语言设计上最干净的实现。

哲学 2:不拥有是最高级的观察

weak_ptr 的语义三角:观察(看) + 不拥有(不控制生命周期) + 可检测(能知道对方死了)。这三条缺一条就不是完整的「观察」——裸指针只能看但不能检测,shared_ptr 能检测但会控制生命周期。只有 weak_ptr 三者俱备。这和数据库的外键约束(引用完整性)、微服务的健康检查(存活探测)、操作系统的 pid 检测——所有「非拥有引用」的系统共享一套哲学。

哲学 3:CRTP 是为了在编译期消去虚函数——零代价的类型恢复

enable_shared_from_this<Derived> 的 CRTP 设计让 shared_from_this() 的返回类型在编译期就确定为 shared_ptr<Derived>——不需要虚函数、不需要 RTTI、不需要 dynamic_cast。如果设计方案是用普通基类 + 虚函数做类型恢复,每个 enable_shared_from_this 对象要多 8 字节的 vptr。CRTP 把这笔代价从运行时搬到了编译期——零开销抽象的经典案例。

哲学 4:惰性清理——不要急,让自然淘汰发生

Observer 列表的惰性清理——不在 detach 时遍历删除,而在 notify 时 lock() 发现失效才删。这个设计模式可以推广到任何「列表+弱引用」的场景:线程池、连接池、事件总线、缓存淘汰。原则是:在访问时做清理——把清理成本分摊到每次遍历中,而不是创建额外的清理事件。

哲学 5:控制块的独立存活——对象死了,墓碑还在

weak_ptr 的安全保证有一个物理前提——控制块在堆上独立分配,在 strong_count=0(对象析构)后继续存活(由 weak_count 决定生命周期)。这就是为什么 alive_ flag 方案永远做不到真正的安全——flag 和对象在同一片内存上。安全检测需要物理上独立的标记——这是软件架构中「元数据和数据分离」原则在 C++ 内存管理中的直接体现。

# 10.4 速查表合集

this 传递方式安全等级:

方式 安全性 阻止对象释放 可检测失效
裸 this ❌ ❌ ❌
shared_ptr<T>(this) ❌ (double free) ✅ —
shared_from_this() ✅ ✅ —
weak_from_this() ✅ ❌ ✅ (lock)

enable_shared_from_this 使用决策:

对象是否由 shared_ptr 管理?
├─ 否 → 不要继承 enable_shared_from_this
└─ 是 → 是否需要从 this 获取 shared_ptr/weak_ptr?
        ├─ 否 → 不需要继承
        └─ 是 → 继承 enable_shared_from_this ✅
                 ├─ 需要持有 → shared_from_this()
                 └─ 只需观察 → weak_from_this()
1
2
3
4
5
6
7

下一篇:this 的增强方案说清了。下一篇进入 31.五种存储期管理——static/thread/automatic/dynamic/temporary 五种存储期的完整对比、静态局部变量的线程安全初始化机制(双重检查锁汇编)、TLS 在 Linux/Windows 的不同实现。

上次更新: 2026/06/10, 11:13:41
shared_ptr底层剖析
五种存储期管理

← shared_ptr底层剖析 五种存储期管理→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式