文档目录

一、循环引用崩溃现场

struct Node {
    shared_ptr<Node> next;
};

auto a = make_shared<Node>();
auto b = make_shared<Node>();
a->next = b;
b->next = a;  // 💥 循环引用!
a.ptr → [Node A | next→b的control_block]    ref_count_A = 2 (a + b->next)
b.ptr → [Node B | next→a的control_block]    ref_count_B = 2 (b + a->next)

a.reset() → ref_count_A = 1 (还被 b->next 持有) → 不释放
b.reset() → ref_count_B = 1 (还被 a->next 持有) → 不释放
→ 两个节点永远不释放!

二、用 weak_ptr 破解

struct Node {
    weak_ptr<Node> next; // 不增加引用计数!
};

a->next = b;  // weak_ptr = b → weak_count++,ref_count 不变
b->next = a;  // 同上

a.reset() → ref_count_A = 0 → 释放 A → b->next 变成空悬
b.reset() → ref_count_B = 0 → 释放 B
✅ 完美释放!

三、weak_ptr.lock() 的底层安全检查

shared_ptr<Node> sp = weak_node.lock(); // 安全提升
if (sp) {
    // 此时对象一定活着,sp 持有引用计数
}

底层做了什么:

  1. 原子读 control_block.ref_count,如果不为 0。
  2. 原子加 ref_count.fetch_add(1)(lock xadd)。
  3. 返回 shared_ptr(指向对象)。
  4. 如果 ref_count == 0(对象已释放),返回空 shared_ptr。

为什么不直接用 weak_ptr 转 shared_ptr 的拷贝构造?

  • 因为需要检查+增加两个操作的原子性——lock() 内部用 CAS 循环保证不会被并发释放打断。