为何std::shared_ptr管理的对象会发生内存泄漏?
代码示例
#include <iostream> #include <memory> #include <vector> using namespace std; class test : public enable_shared_from_this<test> { vector<shared_ptr<test>> vec; public: void addvec() { vec.emplace_back(shared_from_this()); } ~test() { cout << "destructing\n"; } }; int main() { auto sp = make_shared<test>(); sp->addvec(); // when called, destructor is not invoked and // LeakSanitizer reports a memory leak }
问题描述
一个test对象由std::shared_ptr管理,调用addvec后,内部vector中存储了第二个std::shared_ptr实例(此时引用计数ref_count == 2)。但析构函数从未被调用,LeakSanitizer报告存在内存泄漏,请问这是为什么?
编译命令:g++ -g -fsanitize=leak c.cpp
GCC版本:12.2.1
原因解析
这是典型的shared_ptr循环引用问题,具体逻辑链如下:
main函数中的sp是指向test对象的shared_ptr,此时对象的引用计数为1。- 调用
addvec后,test内部的vec容器存入了一个通过shared_from_this()生成的shared_ptr,该指针同样指向当前test对象,导致引用计数增加到2。 - 当
main函数执行完毕,sp被销毁,引用计数从2减到1。但此时test对象内部的vec还持有一个指向自身的shared_ptr,而vec是test对象的成员变量——只有test对象被销毁时,vec才会被销毁,进而释放它持有的shared_ptr。 - 最终形成死锁:
test对象要销毁,需要引用计数降到0;引用计数要降到0,需要vec里的shared_ptr被释放;vec要释放,又需要test对象先被销毁。循环依赖导致test对象永远无法被析构,内存因此泄漏。
解决方案
将内部持有指针的类型从shared_ptr<test>改为weak_ptr<test>即可解决问题。weak_ptr不会增加对象的引用计数,它仅作为对象的观察者,不会阻止对象被销毁。修改后的成员变量定义如下:
vector<weak_ptr<test>> vec;
调用addvec时无需额外修改,shared_from_this()返回的shared_ptr可以隐式转换为weak_ptr存入容器。这样当外部的sp销毁后,引用计数降到0,test对象会被正常析构,内部的vec也会随之销毁,不会形成循环引用。
内容的提问来源于stack exchange,提问作者Tootsie
相关产品推荐
相关产品推荐

