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

QML与C++交互中std::shared_ptr的生命周期疑问

QML与std::shared_ptr结合的生命周期问题

问题背景

原本在应用中实现了一个Q_INVOKABLE函数,返回由std::shared_ptr管理的QObject(OrderModel)裸指针,代码如下:

namespace tradeclient
{

    class OrderModel : public QObject
    {
        Q_OBJECT

    public:

        Q_PROPERTY(QString marketId READ marketId CONSTANT)
        Q_PROPERTY(quint64 id READ id CONSTANT)

    signals:

        void failed(qint64 code, QString message);

        ...
    };

    using OrderPtr = std::shared_ptr<OrderModel>;

    class MarketModel : public QObject
    {
        Q_OBJECT

    public:

        Q_INVOKABLE tradeclient::OrderModel* createLimitOrder()
        {
            return m_orders.front().get();
        }

    private:

        // 应用中会初始化OrderModel并添加到容器,设置C++所有权
        std::vector<OrderPtr> m_orders;
    };

} //namespace tradeclient

Q_DECLARE_METATYPE(tradeclient::OrderPtr)

为了实现C++和QML同时使用std::shared_ptr,尝试Q_DECLARE_SMART_POINTER_METATYPE后发现无法暴露被包裹对象的属性,于是改用包含std::shared_ptr成员的Q_GADGET:

namespace tradeclient
{
    class OrderGadget
    {
        Q_GADGET

        Q_PROPERTY(tradeclient::OrderModel* p READ pointer);

    public:

        OrderGadget() = default;

        OrderGadget(OrderPtr p) : m_p(std::move(p)) {}

    private:

        tradeclient::OrderModel* pointer()
        {
            return m_p.get();
        }

        OrderPtr m_p;
    };
}

Q_DECLARE_METATYPE(tradeclient::OrderGadget)

修改createLimitOrder()使其返回该gadget:

namespace tradeclient
{
    using OrderPtr = std::shared_ptr<OrderModel>;

    class MarketModel : public QObject
    {
        Q_OBJECT

    public:

        Q_INVOKABLE tradeclient::OrderGadget createLimitOrder()
        {
            return m_orders.front();
        }

    private:

        // 保留默认所有权
        std::vector<OrderPtr> m_orders;
    };

}

在QML中通过p属性访问OrderModel对象,代码可正常打印属性并连接信号:

var shared_order = market.createLimitOrder()

var order = shared_order.p

if (order)
{
    console.log("Qml", "Limit order type: %1, value: %2, id: %3".arg(typeof(order)).arg(JSON.stringify(order)).arg(order.marketId))
    order.failed.connect((code, message) => { window.showFading("%1 order has failed: %2".arg(shared_order.p.marketId).arg(message))})
}
else
    console.log("Qml", "The order is not defined.");

但存在两个疑问:

  1. 是否可能QML的GC在std::shared_ptr销毁OrderModel后才删除order变量?
  2. 引用shared_order的failed信号处理函数的生命周期是怎样的?

解答

疑问1:QML GC是否会在std::shared_ptr销毁OrderModel后才删除order变量?

不会出现这种情况,原因如下:

  • 当QML获取order = shared_order.p时,会自动对OrderModel*裸指针建立QML侧引用,QML的GC会追踪该对象,同时Qt对象所有权机制会增加对象的引用计数(通过隐式所有权管理或父对象机制)。
  • OrderGadget中的std::shared_ptr和C++侧m_orders容器共同持有OrderModel的引用,只有当shared_order被QML GC回收(OrderGadget销毁,std::shared_ptr引用计数减1),且m_orders移除对应OrderPtr(引用计数再减1),才会触发OrderModel销毁。
  • 即使极端情况下std::shared_ptr先销毁OrderModel,QML中的order会变成空悬指针,此时QML访问该对象会直接返回undefined或触发警告,不会等到GC删除变量——QML GC仅管理JS对象生命周期,OrderModel作为C++对象,生命周期由Qt对象系统和std::shared_ptr共同管控。

疑问2:引用shared_order的failed信号处理函数的生命周期?

信号处理函数的生命周期由以下因素决定:

  • 该函数连接的是order.failed信号,只要OrderModel对象存在,连接就会保持。
  • 这个JS匿名函数的生命周期由QML GC管理:只要有信号连接指向它,就不会被回收;当OrderModel销毁时,Qt会自动断开所有信号连接,此时函数失去引用,会被QML GC在合适时机回收。
  • 处理函数中引用了shared_order.p.marketId,只要函数存在,shared_order就会被GC保留,进而OrderGadget实例也会被保留,std::shared_ptr的引用计数维持,间接延长OrderModel的生命周期——直到信号连接断开、处理函数被回收,shared_order才会被GC清理,OrderGadget销毁,std::shared_ptr引用计数减1。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 20:36:38