目次
1.引言
2.QVariant的用法
2.1.包罗头文件
2.2.根本范例的存储与获取
2.3.自定义范例的存储与获取
2.4.枚举范例的存储与获取
2.5.范例查抄与转换
2.6.容器范例的存储与获取
3.枚举的问题
4.信号槽中利用自定义布局体
4.1.利用QVariant转换
4.2.直接传递自定义布局体
5.性能开销
5.1. 内存开销
5.2. 时间开销
5.3. 与直接利用具体范例的对比
6.总结
1.引言
在Qt框架中,QVariant类是一个非常强盛的工具,它答应我们存储各种范例的数据,并在需要时将其转换为原始范例。QVariant类似于C语言中的void*,但它更加范例安全,并且可以或许存储多种数据范例。然而,QVariant在处理惩罚枚举范例(enum)、自定义布局体时存在一些限定。本文将探讨怎样办理这一问题,使QVariant可以或许与枚举范例、自定义布局体协同工作。
2.QVariant的用法
QVariant 是 Qt 中一个非常强盛且灵活的类,它可以存储多种不同范例的数据,并且可以在不同范例之间举行转换。
2.1.包罗头文件
在利用 QVariant 之前,需要包罗相应的头文件:
这一点我是常常忘记,导致编译错误,但是又不是很轻易发现是没有包罗头文件QVariant。
2.2.根本范例的存储与获取
QVariant 可以存储许多根本数据范例,如整数、浮点数、字符串等。
存储根本范例
- #include <QVariant>#include <QDebug>int main() { // 存储整数 QVariant intVariant = 42; // 存储浮点数 QVariant doubleVariant = 3.14; // 存储字符串 QVariant stringVariant = "Hello, World!"; return 0;}
复制代码 获取根本范例
- #include <QVariant>#include <QDebug>int main() { QVariant intVariant = 42; QVariant doubleVariant = 3.14; QVariant stringVariant = "Hello, World!"; // 获取整数 int intValue = intVariant.toInt(); // 获取浮点数 double doubleValue = doubleVariant.toDouble(); // 获取字符串 QString stringValue = stringVariant.toString(); qDebug() << "Int value:" << intValue; qDebug() << "Double value:" << doubleValue; qDebug() << "String value:" << stringValue; return 0;}
复制代码 2.3.自定义范例的存储与获取
对于自定义范例,需要将其注册到 Qt 的元对象体系中,以便 QVariant 可以或许处理惩罚。
定义自定义范例
- #include <QObject>#include <QVariant>#include <QDebug>// 定义自定义范例class MyClass {public: MyClass(int value) : m_value(value) {} int value() const { return m_value; }private: int m_value;};// 注册自定义范例Q_DECLARE_METATYPE(MyClass)int main() { // 存储自定义范例 MyClass myObject(123); QVariant customVariant = QVariant::fromValue(myObject); // 获取自定义范例 if (customVariant.canConvert<MyClass>()) { MyClass retrievedObject = customVariant.value<MyClass>(); qDebug() << "Retrieved value:" << retrievedObject.value(); } return 0;}
复制代码 2.4.枚举范例的存储与获取
对于枚举范例,需要利用 Q_ENUM 宏将其注册到元对象体系中。
- #include <QObject>#include <QVariant>#include <QDebug>// 定义枚举范例class MyEnumClass : public QObject { Q_OBJECTpublic: enum MyEnum { Option1, Option2, Option3 }; Q_ENUM(MyEnum)};int main() { // 存储枚举值 QVariant enumVariant = QVariant::fromValue(MyEnumClass::Option2); // 获取枚举值 if (enumVariant.canConvert<MyEnumClass::MyEnum>()) { MyEnumClass::MyEnum retrievedEnum = enumVariant.value<MyEnumClass::MyEnum>(); qDebug() << "Retrieved enum value:" << retrievedEnum; } return 0;}#include "main.moc"
复制代码 2.5.范例查抄与转换
QVariant 提供了一些方法来查抄其存储的数据范例,并举行范例转换。
- #include <QVariant>#include <QDebug>int main() { QVariant variant = 42; if (variant.type() == QVariant::Int) { qDebug() << "The variant contains an integer."; } if (variant.canConvert(QVariant::String)) { qDebug() << "The variant can be converted to a string."; } return 0;}
复制代码 范例转换
- #include <QVariant>#include <QDebug>int main() { QVariant variant = 42; // 转换为字符串 QString stringValue = variant.toString(); qDebug() << "Converted to string:" << stringValue; return 0;}
复制代码 2.6.容器范例的存储与获取
QVariant 也可以存储 Qt 的容器范例,如 QList、QMap 等。
- #include <QVariant>#include <QList>#include <QDebug>int main() { // 存储QList QList<int> intList = {1, 2, 3}; QVariant listVariant = QVariant::fromValue(intList); // 获取QList if (listVariant.canConvert<QList<int>>()) { QList<int> retrievedList = listVariant.value<QList<int>>(); for (int value : retrievedList) { qDebug() << "List value:" << value; } } return 0;}
复制代码 3.枚举的问题
假设我们有一个简单的枚举范例:
- class EnumValue {
- public:
- enum Values {
- V1 = 100,
- V2,
- V3
- };
- };
复制代码 我们希望将EnumValue::Values枚举范例存储在QVariant中,并在需要时将其取出。我们大概会尝试以下代码:
- QVariant var = EnumValue::V1;
- EnumValue::Values ev = var.value<EnumValue::Values>();
- qDebug() << "var = " << ev;
复制代码 不幸的是,这段代码会导致编译错误:
- 错误: 'qt_metatype_id' is not a member of 'QMetaTypeId<EnumValue::Values>'
复制代码 问题的根源
这个错误的根本缘故原由是,QVariant在存储和取出数据时,依靠于Qt的元对象体系。为了可以或许精确地存储和取出自定义范例(包括枚举范例),这些范例必须被Qt的元对象体系所识别。枚举范例默认情况下并没有被Qt的元对象体系处理惩罚,因此QVariant无法直接存储和取出枚举范例。
办理方案
方法一:逼迫范例转换
一种简单的办理方法是利用逼迫范例转换。由于枚举范例本质上是一个整数,我们可以将QVariant中的值转换为int,然后再将其转换为枚举范例:
- QVariant var = EnumValue::V1;
- EnumValue::Values ev = static_cast<EnumValue::Values>(var.toInt());
- qDebug() << "var = " << ev;
复制代码 这种方法固然可行,但它看起来不够优雅,并且轻易堕落。
方法二:利用Q_DECLARE_METATYPE宏
为了更优雅地办理这个问题,Qt提供了一个宏Q_DECLARE_METATYPE,它可以将自定义范例(包括枚举范例)注册到Qt的元对象体系中。我们只需要在枚举范例定义之后添加这个宏:
- class EnumValue {
- public:
- enum Values {
- V1 = 100,
- V2,
- V3
- };
- };Q_DECLARE_METATYPE(EnumValue::Values)
复制代码 然后,我们可以利用QVariant::fromValue()函数将枚举范例存储在QVariant中,并利用QVariant::value()函数将其取出:
- QVariant var = QVariant::fromValue(EnumValue::V1);
- EnumValue::Values ev = var.value<EnumValue::Values>();
- qDebug() << "var = " << ev;
复制代码 这次,代码将精确输出100,表示我们乐成地将枚举范例存储并从QVariant中取出。
4.信号槽中利用自定义布局体
4.1.利用QVariant转换
起首定义一个布局体:
- // 定义自定义结构体
- struct MyStruct {
- int id;
- QString name;
- };
复制代码 然后用Q_DECLARE_METATYPE(MyStruct)注册自定义布局体
定义一个布局体并发送:
- MyStruct stInfo;
- stInfo.id = 100;
- stInfo.name = "214124";
- emit sig_sendInfo(QVariant::fromValue(stInfo));
复制代码 接收端槽函数处理惩罚如下:
- connect(信号类指针, &信号类::sig_trainInfo, 槽函数指针, [=](QVariant var){
- if (var.canConvert<TRANS_DOT_INFO>())
- {
- MyStruct stInfo = var.value<MyStruct>();
-
- //...
- }
- });
复制代码 4.2.直接传递自定义布局体
在利用 Q_DECLARE_METATYPE 宏声明该布局体为元范例,然后在利用信号槽之前调用 qRegisterMetaType 函数举行注册。
- // 声明元类型
- Q_DECLARE_METATYPE(MyStruct)
- // 在合适的地方(如 main 函数中)进行注册
- int main(int argc, char *argv[]) {
- QApplication a(argc, argv);
- // 注册自定义结构体到元对象系统
- qRegisterMetaType<MyStruct>("MyStruct");
- // 后续代码...
- return a.exec();
- }
复制代码 完备的实例代码如下:
- #include <QObject>#include <QMetaType>#include <QDebug>#include <QApplication>// 定义自定义结构体
- struct MyStruct {
- int id;
- QString name;
- };// 声明元范例Q_DECLARE_METATYPE(MyStruct)// 发送信号的类class Sender : public QObject { Q_OBJECTpublic: explicit Sender(QObject *parent = nullptr) : QObject(parent) {}signals: // 定义包罗自定义布局体参数的信号 void mySignal(const MyStruct &data);public slots: void sendData() { MyStruct data; data.id = 1; data.name = "Example"; // 发射信号 emit mySignal(data); }};// 接收信号的类class Receiver : public QObject { Q_OBJECTpublic: explicit Receiver(QObject *parent = nullptr) : QObject(parent) {}public slots: // 定义槽函数 void handleSignal(const MyStruct &data) { qDebug() << "Received data - ID:" << data.id << ", Name:" << data.name; }};int main(int argc, char *argv[]) { QApplication a(argc, argv); // 注册自定义布局体到元对象体系 qRegisterMetaType<MyStruct>("MyStruct"); // 创建发送者和接收者对象 Sender sender; Receiver receiver; // 连接信号和槽 QObject::connect(&sender, &Sender::mySignal, &receiver, &Receiver::handleSignal); // 触发信号 sender.sendData(); return a.exec();}#include "main.moc"
复制代码 5.性能开销
QVariant 是 Qt 中一个用于存储多种不同数据范例的通用容器类,它为数据的存储和处理惩罚提供了很大的灵活性,但也带来了一定的性能开销,下面从几个方面具体分析:
5.1. 内存开销
- 额外的元数据存储:QVariant 除了要存储实际的数据外,还需要存储关于该数据范例的元信息。这包括数据范例的标识(如 QMetaType::Type),用于在运行时识别存储的数据范例。这种额外的元数据存储会增长内存的利用量,尤其是在需要存储大量 QVariant 对象时,内存开销会更加明显。
- 深拷贝问题:对于一些复杂的数据范例(如自定义类或容器),QVariant 在存储时大概会举行深拷贝。深拷贝意味着会复制整个对象及其所有成员,这会占用额外的内存空间。例如,当存储一个包罗大量元素的 QList 时,QVariant 会复制该列表的所有元素,导致内存利用量明显增长。
5.2. 时间开销
- 范例查抄和转换:在利用 QVariant 时,常常需要举行范例查抄和转换操纵。例如,利用 canConvert 方法查抄是否可以将 QVariant 转换为特定范例,以及利用 toXXX 系列方法(如 toInt、toString)举行范例转换。这些操纵需要在运行时举行范例判断和转换逻辑,会带来一定的时间开销。特别是在频仍举行范例查抄和转换的场景下,性能影响会更加突出。
- QVariant var = "123";
- if (var.canConvert<int>()) {
- int num = var.toInt();
- }
复制代码
- 构造和析构:创建和烧毁 QVariant 对象也会有一定的时间开销。构造 QVariant 对象时,需要初始化其内部的元数据和存储数据;析构时,需要释放相关的资源。对于简单的数据范例,这种开销大概相对较小,但对于复杂的数据范例,开销会相应增长。
5.3. 与直接利用具体范例的对比
- 性能差异:相比于直接利用具体的数据范例,QVariant 的性能要低很多。直接利用具体范例时,编译器可以举行更多的优化,并且不需要举行额外的范例查抄和转换。例如,直接利用 int 范例举行计算的速率会比利用 QVariant 存储 int 范例并举行操纵快得多。
- // 直接使用 int 类型
- int a = 10;
- int b = 20;
- int result = a + b;
- // 使用 QVariant 存储 int 类型
- QVariant varA = 10;
- QVariant varB = 20;
- if (varA.canConvert<int>() && varB.canConvert<int>()) {
- int resultVariant = varA.toInt() + varB.toInt();
- }
复制代码
- 适用场景:固然 QVariant 存在性能开销,但在某些场景下它是非常有用的。例如,当需要处理惩罚多种不同范例的数据,或者在不同模块之间传递数据且数据范例不确定时,QVariant 可以提供很大的便利。但在对性能要求极高且数据范例明白的场景下,应只管制止利用 QVariant。
6.总结
QVariant 是 Qt 框架中一个极为实用的类,它为不同范例数据的存储与处理惩罚提供了统一的办理方案,不妨去试一试吧!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |