将QObject*传递给QML代码时出现double free错误的原因
问题分析:QML中返回栈分配QObject指针导致double free错误
场景重现
正常执行情况
初始C++代码:
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> class Returned : public QObject { Q_OBJECT public: Returned() { qDebug() << "Returned"; } ~Returned() { qDebug() << "~Returned"; } Q_INVOKABLE int invokable() { qDebug() << "invokable"; return 42; } }; class Exposed : public QObject { Q_OBJECT public: Q_INVOKABLE QObject* getSomething() { return &object; } private: Returned object; }; int main(int argc, char* argv[]) { QGuiApplication app(argc, argv); Exposed e; QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("exposed", &e); engine.load("test.qml"); int result; QMetaObject::invokeMethod(e.getSomething(), "invokable", qReturnArg(result)); qDebug() << result; app.exec(); qDebug() << "out of event loop"; } #include "test.moc"
对应QML代码(注释调用行):
import QtQuick import QtQuick.Controls ApplicationWindow { visible: true // THIS IS COMMENTED OUT Text { text: exposed.getSomething().invokable(); } }
程序正常输出:
Returned invokable 42
关闭窗口后无错误退出:
out of event loop ~Returned
触发错误的情况
取消QML中的注释行,修改后的QML代码:
import QtQuick import QtQuick.Controls ApplicationWindow { visible: true Text { text: exposed.getSomething().invokable(); } }
简化后的C++主函数:
int main(int argc, char* argv[]) { QGuiApplication app(argc, argv); Exposed e; QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("exposed", &e); engine.load("../test.qml"); app.exec(); qDebug() << "out of event loop"; }
程序启动时输出:
Returned invokable
窗口显示"42",但关闭窗口后触发错误:
out of event loop ~Returned double free or corruption (out)
程序触发SIGABRT,调用栈如下:
__pthread_kill_implementation (@pthread_kill@@GLIBC_2.34:81) __pthread_kill_internal (@pthread_kill@@GLIBC_2.34:59) __GI___pthread_kill (@pthread_kill@@GLIBC_2.34:59) __GI_raise (@raise:10) __GI_abort (@abort:46) __libc_message (@__libc_message:178) malloc_printerr (@7ffff54a0cfc..7ffff54a0d65:3) _int_free (@_int_free:474) __GI___libc_free (@free:31) Returned::~Returned() (/home/user/work/test/test.cpp:9) QV4::QObjectWrapper::destroyObject(bool) (@QV4::QObjectWrapper::destroyObject(bool):96) QV4::MemoryManager::sweep(bool, void (*)(char const*)) (@QV4::MemoryManager::sweep(bool, void (*)(char const*)):226) QV4::MemoryManager::~MemoryManager() (@QV4::MemoryManager::~MemoryManager():23) QV4::ExecutionEngine::~ExecutionEngine() (@QV4::ExecutionEngine::~ExecutionEngine():281) QJSEngine::~QJSEngine() (@QJSEngine::~QJSEngine():16) main (/home/user/work/test/test.cpp:29) __libc_start_call_main (@__libc_start_call_main:29) __libc_start_main_impl (@__libc_start_main@@GLIBC_2.34:43) _start (@_start:15)
错误原因
问题核心在于QML引擎的内存管理机制:
- 当C++向QML返回
QObject*时,QML默认认为对象是堆分配的,会接管所有权(由QML垃圾回收器负责销毁)。 - 但你的
Returned object是栈分配的成员变量,生命周期由栈对象Exposed e管理,程序退出时会自动调用析构函数。 - 关闭窗口后,QML引擎销毁时,垃圾回收器会尝试
delete这个Returned对象,而该对象已经因栈帧销毁被自动析构过一次,导致双重释放,触发double free错误。
从调用栈可见:QV4::QObjectWrapper::destroyObject调用了Returned的析构函数,之后栈对象的析构又被执行了一次。
解决方案
1. 改为堆分配并设置父对象
将Returned改为堆分配,把Exposed作为其父对象,让QML不接管所有权,父对象销毁时自动清理:
class Exposed : public QObject { Q_OBJECT public: Exposed() : object(new Returned(this)) {} // 设置父对象 Q_INVOKABLE QObject* getSomething() { return object; } private: QScopedPointer<Returned> object; // 智能指针管理堆对象 };
2. 明确设置QML不接管对象所有权
返回对象给QML前,调用QJSEngine::setObjectOwnership指定对象由C++管理:
Q_INVOKABLE QObject* getSomething() { QJSEngine::setObjectOwnership(&object, QJSEngine::CppOwnership); return &object; }
这样QML垃圾回收器不会尝试销毁该对象,避免双重释放。
内容的提问来源于stack exchange,提问作者Ivan
相关产品推荐
相关产品推荐

