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

将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 13:22:35