农妇山泉一亩田 发表于 2025-3-18 10:01:28

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

目次
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 之前,需要包罗相应的头文件:
#include <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企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: QVariant:Qt中万能范例的利用与理解