QML中使用===比较自定义对象的规则疑问及异常现象
QML中自定义值类型的对象比较行为疑惑
我原本预期在QML中比较两个对象时,仅当二者为同一对象时才会返回true,但实际表现不同且不一致。
测试代码说明
自定义值类型定义
通过Q_DECLARE_METATYPE注册了一个包含QString成员的结构体some_type:
#pragma once #include <QString> #include <QMetaType> struct some_type { QString id; some_type() { } some_type(const some_type &v) : id(v.id) { } }; Q_DECLARE_METATYPE(some_type)
模型实现
编写了一个继承自QAbstractListModel的模型,包含返回some_type实例的属性,以及触发携带该类型实例的信号的方法:
#include <QAbstractListModel> #include <QStringList> #include "some_type.h" class AnimalModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(some_type someValue READ someValue NOTIFY someValueChanged) public: enum AnimalRoles { TypeRole = Qt::UserRole + 1, SizeRole }; AnimalModel(QObject *parent = 0); int rowCount(const QModelIndex & parent = QModelIndex()) const; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; Q_INVOKABLE void sendSomeValue(); some_type someValue() const; signals: void sendSomeValueSignal(some_type someValue); void someValueChanged(); protected: QHash<int, QByteArray> roleNames() const; private: QVector<some_type> someValues; };
关键实现部分:
AnimalModel::AnimalModel(QObject *parent) : QAbstractListModel(parent) { someValues << some_type(); someValues[0].id = "abcd"; } some_type AnimalModel::someValue() const { return someValues.first(); } void AnimalModel::sendSomeValue() { QString savedStr = someValues.first().id; some_type someValue = {}; someValue.id = savedStr; //someValue.id.data(); emit sendSomeValueSignal(someValue); }
QML代码
点击ListView空白区域时调用模型方法触发信号,在信号处理器中比较两个some_type对象:
import QtQuick 2.15 ListView { width: 200; height: 250 required model Connections { target: model function onSendSomeValueSignal(value) { var currentValue = model.someValue if (value === currentValue) { console.log("Same") } else { console.log("Different") } } } MouseArea { anchors.fill: parent onClicked: { model.sendSomeValue() } } }
问题现象
原本预期会始终打印Different,因为信号中的value和属性返回的currentValue是两个不同的some_type实例。但实际表现是:
- 未启用
someValue.id.data()时,打印Same - 启用该行代码后,打印
Different
在另一个大型项目中,该表现更不稳定:有时内部QString内容相同的不同some_type对象比较返回false,有时返回true。
原因解析
这一现象和Qt的写时复制(COW)机制以及QML对值类型的比较规则有关:
some_type是值类型(仅通过Q_DECLARE_METATYPE注册,未使用Q_GADGET或Q_OBJECT),QML对值类型的===比较是逐成员比较。QString是典型的COW类型:复制QString时,底层不会立即复制内存,只会共享数据指针。QML比较QString时,会优先比较内部数据指针而非逐字符比较内容——当两个some_type的id共享同一个QString数据指针时,QML就认为这两个some_type对象相等。- 调用
someValue.id.data()(非const方法)会触发COW的写操作,强制QString复制底层内存,此时两个id的数据指针不再相同,QML就认为对象不等。 - 大型项目中表现不稳定,是因为COW的触发时机不可控:其他业务逻辑可能会对QString执行修改操作,或触发写复制,导致数据指针时而共享、时而独立,最终比较结果不稳定。
明确规则与解决方案
QML对象比较的明确规则
- QObject派生的引用类型:
===比较的是对象身份,即是否为同一个实例。 - 值类型:
===默认按成员逐值比较,但对于Qt的COW类型(QString、QByteArray等),会优化为比较内部数据指针,仅当指针不同时才会逐内容比较。
稳定比较的解决方案
要让自定义值类型的比较行为可控,需为其添加Q_GADGET并实现operator==,让QML调用自定义的比较逻辑:
#pragma once #include <QString> #include <QMetaType> #include <QObject> struct some_type { Q_GADGET QString id; some_type() = default; some_type(const some_type &v) : id(v.id) {} // 自定义比较逻辑,按id内容比较 bool operator==(const some_type &other) const { return id == other.id; } // 可选:暴露id成员给QML访问 Q_PROPERTY(QString id MEMBER id) }; Q_DECLARE_METATYPE(some_type)
如果需要严格区分不同实例(即使内容相同也认为不等),可以将some_type改为QObject派生的引用类型,但值类型场景下通常不需要这种设计。
内容的提问来源于stack exchange,提问作者Rasheduzzaman Sourov
相关产品推荐
相关产品推荐

