You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

shared_ptr拷贝构造函数工作原理及多线程并发操作问题咨询

关于shared_ptr的两个常见问题解答

1. shared_ptr的拷贝构造函数是如何工作的?

其实shared_ptr内部藏着两个核心组件:一个是指向你实际堆对象的指针,另一个是指向控制块的指针——这个控制块就像个小管家,里面存着引用计数、对象的析构函数,还有其他管理信息。

当你用一个已有的shared_ptr去拷贝构造新的shared_ptr时,流程是这样的:

  • 新的shared_ptr会复制原shared_ptr里的「对象指针」和「控制块指针」
  • 然后原子地把控制块里的引用计数加1(原子操作是关键,保证多线程下计数不会出现混乱)
  • 要注意,只有第一次创建shared_ptr(比如直接用new出来的指针构造,或者用make_shared)的时候,才会生成这个控制块。之后所有拷贝、赋值操作都是共享同一个控制块。

举个简单的代码例子:

auto p1 = make_shared<int>(42); // 生成控制块,引用计数初始为1
auto p2 = p1; // 拷贝构造,控制块里的计数原子加1,变成2

这样一来,只要还有至少一个shared_ptr指向控制块,控制块和它管理的堆对象就不会被销毁。

2. 主线程销毁shared_ptr导致引用计数降至0,且正处于释放其指向的堆内存的过程中,此时另一个线程正在执行shared_ptr的拷贝构造操作,会发生什么?

这个问题得分两种核心情况来看:

情况一:另一个线程拷贝的是已失效的shared_ptr对象

如果主线程里的那个shared_ptr已经走到销毁流程(比如出了作用域,正在执行析构函数),此时它本身已经是一个无效的对象了。这时候另一个线程如果去拷贝这个已经失效的shared_ptr,属于未定义行为——可能程序直接崩溃,可能出现奇怪的内存错误,也可能看起来没事但埋下隐患,完全没法预测结果。

情况二:另一个线程拷贝的是仍有效的shared_ptr对象

如果另一个线程拷贝的是另一个还活着的shared_ptr(比如之前拷贝出去的p2),那主线程销毁p1的时候,引用计数只会从2减到1,不会触发内存释放。这种情况下一切正常,完全没有问题。

那如果真的出现「主线程刚把计数减到0,开始释放对象,另一个线程刚好要拷贝」的极端场景呢?其实这里的关键是:引用计数的增减是原子操作。主线程在销毁shared_ptr时,会先原子地把计数减1,只有当减完之后计数等于0,才会进入释放对象的流程。如果另一个线程在主线程减计数之前完成了拷贝(也就是原子加1),那主线程减完计数后是1,不会触发释放;如果主线程已经减到0并开始释放,那此时已经没有有效的shared_ptr指向这个对象了——另一个线程能拷贝的只能是失效的shared_ptr,回到情况一的未定义行为。

总结一下:只要你保证多线程下拷贝的都是当前有效的shared_ptr对象,就不会有问题;但如果拷贝的是正在被销毁的、已经失效的shared_ptr,那就是未定义行为,绝对要避免。

内容的提问来源于stack exchange,提问作者Bryan Fok

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:29:00