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

使用std::vector和std::shared_ptr出现疑似内存泄漏,是否为bug?

问题分析:循环引用导致的shared_ptr引用计数异常

首先可以明确的是,这不是Visual Studio 2017的bug,而是典型的std::shared_ptr循环引用陷阱,和std::vector本身无关。让我们一步步拆解问题:

为什么离开作用域后data的use_count保持为9?

你的结构体Foo存在一个隐形的循环引用链:

  1. 局部Foo实例持有std::shared_ptr<std::vector<Foo>> foos,这个指针指向包含8个Foo拷贝的vector;
  2. vector中的每个Foo拷贝,又都持有同一个foos指针(指向该vector);
  3. 同时,所有Foo实例(局部实例+vector中的8个拷贝)的data都指向同一个int对象,所以初始use_count是9。

当局部Foo实例离开作用域时:

  • 它的data和foos会被销毁,data的引用计数减1(理论上变成8),foos的引用计数也减1;
  • 但vector中的8个Foo拷贝还各自持有foos指针,这意味着vector的shared_ptr引用计数还有8,远大于0——vector因此不会被销毁,里面的8个Foo拷贝也会一直存在,它们的data指针持续引用着int对象,所以data的use_count始终保持在较高数值(你看到的9可能是代码细节上的差异,比如vector中包含了局部实例本身)。

简单说:vector和它内部的Foo实例互相持有shared_ptr,形成了闭环,导致双方的引用计数都无法降到0,内存无法自动释放。

为什么手动添加析构函数重置data后恢复正常?

当你在Foo的析构函数中主动重置vector里所有元素的data指针时:

  • 这一步直接减少了data的引用计数(一次性减8);
  • 同时,当局部Foo的foos被销毁后,vector的引用计数降到0,vector被销毁,里面的Foo拷贝也会被析构,此时data的引用计数就会降到预期的1(如果还有其他残留引用)或0(完全释放)。

更优雅的解决方案:用std::weak_ptr打破循环

手动重置指针的方式不够优雅,也容易遗漏。正确的做法是把Foo中的foos类型从std::shared_ptr<std::vector<Foo>>改成std::weak_ptr<std::vector<Foo>>:

struct Foo {
    std::shared_ptr<int> data;
    std::weak_ptr<std::vector<Foo>> foos;
};

std::weak_ptr不会增加所指向对象的引用计数,它只是一个“观察者”指针。这样一来:

  • 局部Foo的shared_ptr<std::vector<Foo>>是唯一持有vector的强引用;
  • 当局部Foo离开作用域,foos的强引用被销毁,vector的引用计数降到0,vector和内部的所有Foo拷贝都会被自动销毁;
  • 所有data指针的引用计数也会随之降到0,int对象被正确释放,不会出现引用计数残留的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:49:30