控制块在SharedPtr中的作用(C++)
一句话先给结论:
控制块(Control Block)是 shared_ptr 的“户口本”——它记录引用计数、托管对象指针、自定义删除器、自定义分配器以及 weak_ptr 的额外信息;所有指向同一对象的 shared_ptr / weak_ptr 共用同一份控制块,从而保证“最后一个离开的人关灯”。
1️⃣ 控制块里到底放了什么?
字段 作用
T* ptr
真正托管的裸指针
size_t shared_count
当前存活 shared_ptr 个数
size_t weak_count
当前存活 weak_ptr 个数
Deleter deleter
用户自定义删除器(默认为 delete
)
Allocator allocator
用户自定义分配器
可选额外信息 type-erased 的 deleter / allocator、控制块自身内存布局等
2️⃣ 生命周期规则(口诀版)
- shared_count == 0 → 销毁托管对象(调用 deleter)
- shared_count + weak_count == 0 → 销毁控制块(释放内存)
因此:
- 只要还有 任意 shared_ptr,托管对象 不会 被销毁。
- 只要还有 任意 weak_ptr,控制块 不会 被销毁(weak_ptr 要查过期状态)。
3️⃣ 一张时序图
make_shared<T>() ─┐├─► [Control Block: ptr=T*, shared=1, weak=0]
shared_ptr<T> p ──┘shared_ptr<T> q = p; // shared=2
weak_ptr<T> w = p; // weak=1
p.reset(); // shared=1
q.reset(); // shared=0 → 调用 deleter 销毁 T
w.expired(); // 返回 false,weak=1 仍保活控制块
w.reset(); // weak=0 → 销毁控制块
4️⃣ 与 make_shared
/ shared_ptr<T>(new T)
的细微差别
构造方式 控制块与对象内存布局
std::shared_ptr<T>(new T)
两次分配:先 new T,再 new ControlBlock → 可能 cache miss
std::make_shared<T>(...)
一次分配:一块连续内存里先放 T,再放 ControlBlock → 更快、省一次 new
5️⃣ 手写玩具代码回顾
template <typename T>
struct ControlBlock {T* ptr;size_t shared = 0;size_t weak = 0;/* optional deleter/allocator */
};
所有 SharedPtr<T>
/ WeakPtr<T>
拷贝时只改同一个 ControlBlock*
里的计数,对象与内存的“生死大权”全由控制块说了算。
✅ 一句话记住
控制块 = shared_ptr 的“中控室”;对象何时死、内存何时还,全看这间屋子里两个计数器。