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

为什么main函数结束时shared_ptr没有超出作用域被销毁?

C++ shared_ptr 循环引用与作用域规则解答

核心理解疏漏澄清

  • 作用域规则仅适用于在代码块中声明的栈上自动变量,堆上动态分配的对象及其成员没有「超出作用域」的概念,其生命周期完全由shared_ptr的引用计数控制,和main函数是否退出没有直接关联。

代码执行流程拆解

你的代码中关键对象的存储逻辑和引用计数变化如下:

  1. main函数中声明的auto w是栈上的std::shared_ptr<Widget>类型自动变量,作用域确实是main函数块。main返回时w会自动析构,将其指向的Widget对象的引用计数减1:
    • 初始时Widget的引用计数为2:栈上w占1,w->mGadget->mWidget占1
    • w析构后,Widget的引用计数剩余1,不满足销毁条件
  2. w->mGadget->mWidget是堆上Gadget对象的成员变量,它的生命周期完全依附于所属的Gadget对象:
    • Gadget对象的引用计数为1,唯一的持有者是堆上Widget对象的mGadget成员
    • 只有Widget对象销毁、mGadget成员析构时,Gadget的引用计数才会减到0并触发销毁
  3. 此时形成循环依赖死锁:Widget等待Gadget销毁来释放自己的最后一个引用,Gadget等待Widget销毁来释放自己的最后一个引用,二者永远不会被C++的自动内存管理机制销毁。

关于「main退出回收所有资源」的说明

你认知里的「main退出所有资源停止存在」是操作系统层面的行为:进程退出时操作系统会回收该进程占用的所有内存、文件句柄、网络端口等系统资源,哪怕代码存在内存泄漏,也不会把泄漏的内存留给其他进程。
但这个回收是操作系统直接抹除进程地址空间,不会执行C++层面的对象析构函数。也就是说这两个循环引用对象的析构函数永远不会被调用,如果你在析构函数里写了持久化数据、释放非内存资源(比如跨进程锁、硬件句柄)等逻辑,这些逻辑会丢失,这就是循环引用带来的实际危害。

解决方案

打破循环引用的标准做法是将循环链路中某一个std::shared_ptr替换为std::weak_ptr,weak_ptr不会增加引用计数,不会影响对象的销毁判断:

struct Gadget
{
    // 改用weak_ptr,不增加Widget的引用计数
    std::weak_ptr<Widget> mWidget;
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 23:06:03