shared_ptr的引用计数操作是线程安全的,但所管理对象的访问不安全;get()返回裸指针会脱离生命周期管理导致UB;weak_ptr::lock()是唯一安全的临时升级方式;对象并发访问需额外同步。

shared_ptr 的引用计数是线程安全的
标准明确保证 shared_ptr 的控制块(即引用计数)的增减操作是原子的,多个线程同时拷贝、赋值或析构不同的 shared_ptr 实例(哪怕它们指向同一对象),不会导致引用计数损坏或崩溃。
但注意:这只是对「计数本身」的安全,不是对「所管理对象」的安全。常见误判就在这里。
- 多个线程对同一个
shared_ptr对象(而非不同副本)做赋值或 reset —— 不安全,需外部同步 - 多个线程通过各自持有的
shared_ptr副本,同时读写其指向的底层对象 —— 完全不安全,和shared_ptr无关,取决于对象本身的线程安全性 -
shared_ptr构造/拷贝/析构过程中的内存分配(如首次创建时 new 控制块)由实现决定,但标准要求这些操作在多线程下仍保持引用计数正确性
为什么 get() + raw pointer 会破坏线程安全假象
一旦调用 get() 拿到裸指针,你就脱离了 shared_ptr 的生命周期管理。此时即使其他线程还在用 shared_ptr,该对象也可能在任意时刻被销毁(最后一个 shared_ptr 析构触发 delete)。
典型错误模式:
立即学习“C++免费学习笔记(深入)”;
shared_ptrp = make_shared (42); int* raw = p.get(); // 危险! // ... 其他线程可能已让 p 离开作用域,对象已被释放 cout << *raw; // UB:野指针访问
-
get()不增加引用计数,也不延长对象寿命 - 若需临时传裸指针且确保对象存活,应传
shared_ptr本身(或 const ref),让接收方自己决定是否延长生命周期 - 某些 API 强制要裸指针?考虑用
weak_ptr::lock()动态验证有效性,而不是无条件信任get()
weak_ptr::lock() 是唯一安全的“按需升级”方式
当需要从弱引用(weak_ptr)临时获得强引用并访问对象时,必须用 lock(),它原子地检查对象是否还活着,并返回新的 shared_ptr(若存活)或空 shared_ptr(若已销毁)。
-
lock()是原子操作:检查 + 增引用计数一步完成,不存在竞态窗口 - 直接用
weak_ptr::operator->()或get()是未定义行为,标准不提供该接口 - 即使
lock()成功,返回的shared_ptr只保证「构造完成那一刻」对象存在;之后仍需自行保护对象内容的并发访问
对象访问安全得靠你自己,不是 shared_ptr 的责任
shared_ptr 解决的是「资源何时释放」的问题,不是「资源怎么用」的问题。它不提供任何互斥、读写锁或内存顺序保障。
- 若多个线程通过各自
shared_ptr访问同一个std::vector,你需要手动加std::mutex或使用std::atomic成员 - 若对象本身是只读的(比如配置数据),且初始化后永不修改,则无需额外同步 —— 但这是语义保证,不是
shared_ptr给的 - 注意:
shared_ptr只限制通过该指针修改对象,不阻止其他地方用非 const 指针改;真正不可变需靠设计(如 move-only 初始化 + private mutators)
最容易被忽略的一点:引用计数线程安全 ≠ 对象线程安全,这个边界模糊地带,是绝大多数多线程 shared_ptr bug 的根源。










