使用shared_ptr时程序崩溃,求代码问题分析及修复方案
问题描述
在学习智能指针时,编写的程序在执行g1->outputs_.clear()时崩溃,但替换为g2->inputs_.clear()则正常,相关代码如下:
#include <QVector> #include <memory> using std::shared_ptr; using std::make_shared; class Node; typedef shared_ptr<Node> NodePtr; class Node { public: Node(){}; NodePtr AddInput(NodePtr n){ // 尝试将this转换为shared_ptr,添加到n->outputs_中 NodePtr this_ptr(this); if (!inputs_.contains(n)) { inputs_.append(n); } if (!n->outputs_.contains(this_ptr)) { n->outputs_.append(this_ptr); } return this_ptr; } public: QVector<NodePtr> inputs_; QVector<NodePtr> outputs_; }; int main(){ auto g1 = make_shared<Node>(); auto g2 = make_shared<Node>(); g2->AddInput(g1); g1->outputs_.clear(); // 程序在此处崩溃 return 0; }
崩溃原因
核心问题出在AddInput函数中NodePtr this_ptr(this);这一行:
g2是通过make_shared创建的,已经有一个shared_ptr实例在管理它的生命周期,维护着对应的引用计数。- 直接用裸指针
this构造新的shared_ptr(this_ptr),会创建一个完全独立的智能指针实例,它的引用计数和g2的引用计数毫无关联。 - 当执行
g1->outputs_.clear()时,outputs_中的this_ptr会被销毁,此时这个独立的shared_ptr引用计数降为0,会直接释放g2指向的内存。 - 但
main函数中g2这个智能指针仍然存在,当main结束时,g2的引用计数降为0,会再次尝试释放已经被释放的内存,触发双重释放,导致程序崩溃。
而g2->inputs_.clear()正常的原因是:inputs_中存储的是来自参数的g1的智能指针,它和main中的g1共享同一个引用计数链,clear只会减少引用计数,不会让g1的引用计数降为0,因此不会提前释放内存。
修复方案
要正确获取管理当前对象的shared_ptr,需要让Node类继承std::enable_shared_from_this<Node>,然后通过shared_from_this()方法获取智能指针,这样得到的实例会和原来的shared_ptr共享引用计数。
修复后的代码:
#include <QVector> #include <memory> using std::shared_ptr; using std::make_shared; class Node; typedef shared_ptr<Node> NodePtr; // 继承enable_shared_from_this,用于获取当前对象的shared_ptr class Node : public std::enable_shared_from_this<Node> { public: Node(){}; NodePtr AddInput(NodePtr n){ // 使用shared_from_this()替代直接用this构造 NodePtr this_ptr = shared_from_this(); if (!inputs_.contains(n)) { inputs_.append(n); } if (!n->outputs_.contains(this_ptr)) { n->outputs_.append(this_ptr); } return this_ptr; } public: QVector<NodePtr> inputs_; QVector<NodePtr> outputs_; }; int main(){ auto g1 = make_shared<Node>(); auto g2 = make_shared<Node>(); g2->AddInput(g1); g1->outputs_.clear(); // 不再崩溃 return 0; }
注意事项
shared_from_this()只能在对象已经被shared_ptr管理的情况下调用,也就是必须先通过make_shared或shared_ptr构造函数创建对象的智能指针,之后才能调用该方法,否则会触发未定义行为。- 绝对不要直接用裸指针
this构造shared_ptr,除非能确保该裸指针没有被其他shared_ptr管理,否则必然会导致引用计数混乱和内存错误。
内容的提问来源于stack exchange,提问作者Morry
相关产品推荐
相关产品推荐

