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

如何初始化std::map<QString,std::shared_ptr<A>>?析构函数为何提前调用?

问题分析与解决方案

首先,你的代码出现dtorB在main启动前调用的核心原因是全局变量初始化时机与Qt的初始化顺序冲突,再加上一些潜在的未定义行为。我们一步步拆解:

为什么dtorB会提前被调用?

你的stringToA是全局变量,它的初始化发生在main函数执行之前,但Qt的核心资源(比如QString依赖的字符串处理机制)要等到QCoreApplication或QApplication实例化后才会完全初始化。

当你在全局初始化列表中构造QString("B")和QString("C")时,此时Qt的运行环境还没准备好,这会导致未定义行为——在你的案例中,构造第一个键值对时可能触发了隐性的错误(比如QString内部构造失败),导致这个临时的键值对被丢弃,而与之关联的std::shared_ptr<B>会因引用计数归零而销毁B对象,于是你看到了dtorB和dtorA提前输出。

另外,虽然std::shared_ptr即使基类析构非虚也能正确销毁派生类对象,但你的A类析构函数没有声明为virtual,这是不良实践——如果后续用原始指针操作派生类对象,会导致内存泄漏。

如何正确初始化存储std::shared_ptr的std::map?

要解决这个问题,核心是避免在Qt应用初始化前使用Qt类,同时遵循智能指针的最佳实践:

方案1:延迟初始化(推荐)

将map的初始化移到main函数中,确保在QCoreApplication创建之后再构造Qt对象和智能指针:

#include <map>
#include <memory>
#include <QString>
#include <QDebug>
#include <QCoreApplication>

class A{
public:
    A(){qDebug()<<"ctorA";}
    virtual ~A(){qDebug()<<"dtorA";} // 声明为虚析构函数
};
class B : public A {
public:
    B(){qDebug()<<"ctorB";}
    ~B(){qDebug()<<"dtorB";}
};
class C : public A {
public:
    C(){qDebug()<<"ctorC";}
    ~C(){qDebug()<<"dtorC";}
};

// 声明全局变量但不初始化
std::map<QString, std::shared_ptr<A>> stringToA;

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv); // 先初始化Qt应用
    // 此时Qt环境已准备好,安全构造map
    stringToA = {
        {QString("B"), std::make_shared<B>()},
        {QString("C"), std::make_shared<C>()}
    };
    qDebug()<<"start of programm";
    return a.exec();
}

方案2:使用std::make_shared替代直接new

即使你必须在某个时机初始化,也要用std::make_shared来创建智能指针——它比直接new更安全:如果构造过程中抛出异常,make_shared会自动释放已分配的内存,避免泄漏。比如:

// 正确的元素构造方式
{QString("B"), std::make_shared<B>()}

方案3:避免全局变量(最佳实践)

全局变量容易引发初始化顺序问题,尽量将map放在main函数内,或者封装到类中作为成员变量,通过线程安全的单例模式来访问。

运行修正后的代码,你会看到输出顺序变为:

start of programm
// 程序退出时才会析构
dtorB
dtorA
dtorC
dtorA

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:54:57