QVariant:Qt中万能范例的利用与理解

打印 上一主题 下一主题

主题 951|帖子 951|积分 2853

目次
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 之前,需要包罗相应的头文件:
  1. #include <QVariant>
复制代码
这一点我是常常忘记,导致编译错误,但是又不是很轻易发现是没有包罗头文件QVariant。
2.2.根本范例的存储与获取

QVariant 可以存储许多根本数据范例,如整数、浮点数、字符串等。
存储根本范例
  1. #include <QVariant>#include <QDebug>int main() {    // 存储整数    QVariant intVariant = 42;    // 存储浮点数    QVariant doubleVariant = 3.14;    // 存储字符串    QVariant stringVariant = "Hello, World!";    return 0;}
复制代码
获取根本范例
  1. #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 可以或许处理惩罚。
定义自定义范例
  1. #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 宏将其注册到元对象体系中。
  1. #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 提供了一些方法来查抄其存储的数据范例,并举行范例转换。
  1. #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;}
复制代码
范例转换
  1. #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 等。
  1. #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.枚举的问题

假设我们有一个简单的枚举范例:
  1. class EnumValue {
  2. public:
  3.     enum Values {
  4.         V1 = 100,
  5.         V2,
  6.         V3
  7.     };
  8. };
复制代码
我们希望将EnumValue::Values枚举范例存储在QVariant中,并在需要时将其取出。我们大概会尝试以下代码:
  1. QVariant var = EnumValue::V1;
  2. EnumValue::Values ev = var.value<EnumValue::Values>();
  3. qDebug() << "var = " << ev;
复制代码
不幸的是,这段代码会导致编译错误:
  1. 错误: 'qt_metatype_id' is not a member of 'QMetaTypeId<EnumValue::Values>'
复制代码
问题的根源
这个错误的根本缘故原由是,QVariant在存储和取出数据时,依靠于Qt的元对象体系。为了可以或许精确地存储和取出自定义范例(包括枚举范例),这些范例必须被Qt的元对象体系所识别。枚举范例默认情况下并没有被Qt的元对象体系处理惩罚,因此QVariant无法直接存储和取出枚举范例。
办理方案
方法一:逼迫范例转换
一种简单的办理方法是利用逼迫范例转换。由于枚举范例本质上是一个整数,我们可以将QVariant中的值转换为int,然后再将其转换为枚举范例:
  1. QVariant var = EnumValue::V1;
  2. EnumValue::Values ev = static_cast<EnumValue::Values>(var.toInt());
  3. qDebug() << "var = " << ev;
复制代码
这种方法固然可行,但它看起来不够优雅,并且轻易堕落。
方法二:利用Q_DECLARE_METATYPE宏
为了更优雅地办理这个问题,Qt提供了一个宏Q_DECLARE_METATYPE,它可以将自定义范例(包括枚举范例)注册到Qt的元对象体系中。我们只需要在枚举范例定义之后添加这个宏:
  1. class EnumValue {
  2. public:
  3.     enum Values {
  4.         V1 = 100,
  5.         V2,
  6.         V3
  7.     };
  8. };Q_DECLARE_METATYPE(EnumValue::Values)
复制代码
然后,我们可以利用QVariant::fromValue()函数将枚举范例存储在QVariant中,并利用QVariant::value()函数将其取出:
  1. QVariant var = QVariant::fromValue(EnumValue::V1);
  2. EnumValue::Values ev = var.value<EnumValue::Values>();
  3. qDebug() << "var = " << ev;
复制代码
这次,代码将精确输出100,表示我们乐成地将枚举范例存储并从QVariant中取出。
4.信号槽中利用自定义布局体

4.1.利用QVariant转换

起首定义一个布局体:
  1. // 定义自定义结构体
  2. struct MyStruct {
  3.     int id;
  4.     QString name;
  5. };
复制代码
然后用Q_DECLARE_METATYPE(MyStruct)注册自定义布局体
定义一个布局体并发送:
  1. MyStruct stInfo;
  2. stInfo.id = 100;
  3. stInfo.name = "214124";
  4. emit sig_sendInfo(QVariant::fromValue(stInfo));
复制代码
接收端槽函数处理惩罚如下:
  1. connect(信号类指针, &信号类::sig_trainInfo, 槽函数指针, [=](QVariant var){
  2.         if (var.canConvert<TRANS_DOT_INFO>())
  3.         {
  4.             MyStruct stInfo = var.value<MyStruct>();
  5.             //...
  6.         }
  7.     });
复制代码
4.2.直接传递自定义布局体

在利用 Q_DECLARE_METATYPE 宏声明该布局体为元范例,然后在利用信号槽之前调用 qRegisterMetaType 函数举行注册。
  1. // 声明元类型
  2. Q_DECLARE_METATYPE(MyStruct)
  3. // 在合适的地方(如 main 函数中)进行注册
  4. int main(int argc, char *argv[]) {
  5.     QApplication a(argc, argv);
  6.     // 注册自定义结构体到元对象系统
  7.     qRegisterMetaType<MyStruct>("MyStruct");
  8.     // 后续代码...
  9.     return a.exec();
  10. }
复制代码
完备的实例代码如下:
  1. #include <QObject>#include <QMetaType>#include <QDebug>#include <QApplication>// 定义自定义结构体
  2. struct MyStruct {
  3.     int id;
  4.     QString name;
  5. };// 声明元范例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)举行范例转换。这些操纵需要在运行时举行范例判断和转换逻辑,会带来一定的时间开销。特别是在频仍举行范例查抄和转换的场景下,性能影响会更加突出。
  1.     QVariant var = "123";
  2.     if (var.canConvert<int>()) {
  3.         int num = var.toInt();
  4.     }
复制代码


  • 构造和析构:创建和烧毁 QVariant 对象也会有一定的时间开销。构造 QVariant 对象时,需要初始化其内部的元数据和存储数据;析构时,需要释放相关的资源。对于简单的数据范例,这种开销大概相对较小,但对于复杂的数据范例,开销会相应增长。
5.3. 与直接利用具体范例的对比



  • 性能差异:相比于直接利用具体的数据范例,QVariant 的性能要低很多。直接利用具体范例时,编译器可以举行更多的优化,并且不需要举行额外的范例查抄和转换。例如,直接利用 int 范例举行计算的速率会比利用 QVariant 存储 int 范例并举行操纵快得多。
  1.     // 直接使用 int 类型
  2.     int a = 10;
  3.     int b = 20;
  4.     int result = a + b;
  5.     // 使用 QVariant 存储 int 类型
  6.     QVariant varA = 10;
  7.     QVariant varB = 20;
  8.     if (varA.canConvert<int>() && varB.canConvert<int>()) {
  9.         int resultVariant = varA.toInt() + varB.toInt();
  10.     }
复制代码


  • 适用场景:固然 QVariant 存在性能开销,但在某些场景下它是非常有用的。例如,当需要处理惩罚多种不同范例的数据,或者在不同模块之间传递数据且数据范例不确定时,QVariant 可以提供很大的便利。但在对性能要求极高且数据范例明白的场景下,应只管制止利用 QVariant。
6.总结

   QVariant 是 Qt 框架中一个极为实用的类,它为不同范例数据的存储与处理惩罚提供了统一的办理方案,不妨去试一试吧!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农妇山泉一亩田

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表