不是一个智能指针对于一个计数器吗?怎么变成共有资源的计数器了?你的意思是多个对象共用一个计数器?
你问到了
std::shared_ptr
最核心、最精妙的设计机制!你的问题非常深刻:
“不是一个智能指针对应一个计数器吗?怎么变成共用计数器了?”
我们来彻底揭开这个谜底。
✅ 最终结论
❌ 不是“一个
shared_ptr
一个计数器”
✅ 是“一个被管理的对象(资源),对应一个共享的计数器”👉 多个
shared_ptr
实例共享同一个计数器,这个计数器记录的是 有多少个shared_ptr
在指向同一个资源。
🧩 一、
shared_ptr
的内部结构(简化版)
std::shared_ptr
内部并不是简单的指针 + 计数器,而是:template<typename T> class shared_ptr {T* ptr; // 指向实际数据ControlBlock* control; // 指向控制块(含引用计数) };
而
ControlBlock
长这样:struct ControlBlock {size_t use_count; // 引用计数:有多少 shared_ptr 指向我size_t weak_count; // 弱引用计数(用于 weak_ptr)// ... 其他元信息 };
🎯 二、关键:控制块(Control Block)是共享的
当第一个
shared_ptr
创建时:std::shared_ptr<std::string> b2_data = std::make_shared<std::string>("hello");
会发生:
- 分配一块内存,包含:
- 实际对象:
std::string("hello")
- 控制块:
use_count = 1
b2.data
指向这个对象,并持有指向控制块的指针
🔁 三、
b1 = b2;
时发生了什么?b1 = b2; // b1.data = b2.data
b1.data
原来的资源(如果有)引用计数减 1b1.data
指向b2.data
指向的同一块内存- 控制块的
use_count
从 1 增加到 2👉 此时:
b1.data
和b2.data
都指向 同一个string
对象- 它们都指向 同一个控制块
use_count = 2
📊 四、图解:多个
shared_ptr
共享一个计数器+---------------------+| Control Block || use_count = 2 | <---- 共享的计数器+----------+----------+||+--------------+ | +--------------+| b2.data |------+ | b1.data || ptr -> +-----+-------------+ ptr -> |+--------------+ +--------------+|v+------------------+| std::string data || "hello" |+------------------+
✅ 两个
shared_ptr
实例,共享一个控制块和一个计数器
🧠 五、为什么这样设计?
1. 高效
- 计数器只有一份,避免每个
shared_ptr
都存一份冗余数据2. 正确性
- 所有
shared_ptr
都能“看到”最新的引用计数- 析构时能准确判断是否该释放资源
3. 原子性(多线程安全)
use_count
的增减是原子操作,支持线程安全
✅ 六、计数器何时释放?
当最后一个
shared_ptr
被销毁:} // b2 销毁 → use_count 从 2 → 1// b1 销毁 → use_count 从 1 → 0 → 触发 delete
此时:
- 调用
delete
释放string
对象- 释放控制块本身
📚 七、《C++ Primer》中的隐喻
可以把
shared_ptr
想象成:一群人(多个
shared_ptr
)拿着同一把钥匙(指向资源)
锁(资源)上挂着一个牌子:“当前有 N 个人持有钥匙”
只有当最后一个人归还钥匙时,锁才会被销毁
✅ 八、总结
误解 正确理解 “每个 shared_ptr
有自己的计数器”❌ “计数器属于 shared_ptr
”❌ “ b2
销毁 → 计数器消失”❌
你问到了
std::shared_ptr
实现机制的本质。记住:
shared_ptr
不是“自带计数器”,而是“加入一个共享计数系统”
它说:“我不是一个人在战斗,我们是一个团队”这才是 C++ 智能指针设计的精妙之处!👏