QML与C++交互中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.");
但存在两个疑问:
- 是否可能QML的GC在
std::shared_ptr销毁OrderModel后才删除order变量? - 引用
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
相关产品推荐
相关产品推荐

