关于QVariant 的学习一点分享

关于QVariant 的学习一点分享

本人就像铁匠铺里的学徒,一点一滴地积累Qt开发的一些技巧。看到一个Demo程序中用到了QVariant 进行传值。就像搞明白它。分析了网上大牛们的介绍,于是,自己也想拙笔写一篇。

Variant中文翻译为“变体,转化”。

这个数据类型的出现,有什么意义呢?

那么还是查阅的Qt官方的开发手册吧!

Variant类的开发手册解读

1、因为C++ 是禁止使用 Unions 联合体的,多以大多数的Qt的来不能被以联合体的数据类型来使用。

2、一个Qvariant 对象可以是某个数据类型,或者是很拥有很多值的数据,比如a string list(字符串列表)。

3、Variant 的数据类型很灵活,你可以find out(查明)该对象是何种数据类型,然后转化成另外一个类型。

举例如下:


    QVariant v(123);                // The variant now contains an int
    int x = v.toInt();              // x = 123
    qDebug() << v;                  // Writes a type tag and an int to out
    v = QVariant("hello");          // The variant now contains a QByteArray

    int y = v.toInt();              // y = 0 since v cannot be converted to an int
    QString s = v.toString();       // s = tr("hello")  (see QObject::tr())
    qDebug() << v;                 
    qDebug() << s;
    qDebug() << QString ::number(y);

输出:
在这里插入图片描述

从上面的例子中可以看到, QVariant v 可以store 各种数据类型。不然如何被叫做“变体”的变量呢?

4、当然,QVariant 还支持复杂类型的数据类型。如QList、QMap<QString, QVariant> 、支持结构体等等。所以功能非常强大。

举例:

    QVariant v;
    QStringList sl;  //创建一个字符串列表
       sl<<"A"<<"B"<<"C"<<"D";
       v = QVariant(sl);  //将该列表保存在一个QVariant变量中
       //QVariant::type()函数返回存储在QVariant变量中的值的数据类型。
       if(v.type() == QVariant::StringList)
       {
           QStringList list=v.toStringList();
           for(int i=0;i<list.size();++i)
           {
               qDebug()<<list.at(i);  //输出列表内容
           }
       }

输出:
在这里插入图片描述

结构体应用举例:

先构造一个结构,并声明

struct myStruct //自定义的数据类型
{
    int age;
    char name[10];
};
Q_DECLARE_METATYPE(myStruct)  //注册,必不可少

然后使用QVariant

   int id=qRegisterMetaType<MyStruct>();
           MyStruct stu;
           stu.age = 100;//也可以先定义变量后这样赋值
           strcpy(stu.name,"Hello./n");

           v.setValue(stu); //设置QVariant的值
           qDebug()<<"v:"<<v;

           MyStruct ste; //这部分代码主要是将QVariant类再转化为myStruct类,其他QVariant类转化成其他类也可用这种方法
           ste=v.value<MyStruct>();
           qDebug()<<"ste:"<<ste.age<<ste.name;

           if(v.canConvert(id)){
                          qDebug()<<"it can be converted by this means!";
                          MyStruct tempData=v.value<MyStruct>();
                           qDebug()<<"tempData:"<<tempData.age<<tempData.name;
                      }

输出:
在这里插入图片描述
5、QVariant 也支持Null values(空值)

  QVariant x, y(QString()), z(QString(""));
  x.convert(QVariant::Int);
  // x.isNull() == true
  // y.isNull() == true, z.isNull() == false

到这里,对QVariant 的出现,它的用法基本有所了解了,它非常便捷。

当我们要理解一个新的事物时,用生活中常见的事物作类比,会帮助我们加深对这个新事物的理解程度和准确度。

这里参考一位技术大牛的介绍:

*在世界上还没有发明集装箱之前,所有的货物都是直接通过卡车运送至港口,再由港口的装卸工搬运至货船,由于所有的货物大小形状规格不同,导致装至船上的货物远远小于一艘船的实际承载量。而一次远航,就需要花费很长的周期和高昂的开销。对于海运的货物就变得非常昂贵。
而在出现集装箱之后,工厂生产的产品可以直接装在标准的集装箱中,所有的集装箱可以平整的放在运输船上,一艘船便可以运送大量的货物,从而产品价格低廉,增加了各个国家的经济来往,这也改变了世界的经济格局。
那么,你有没有想到,为什么这么简单的设计就改变了世界呢。这源自于集装箱屏蔽了每种产品的大小形状上的差异性,对外提供了一致的形状,那么所有的周边产品,例如港口的起重机,货船的设计都可以依据集装箱的标准而进行有效的设计。
好了,现在我们回到QVariant类(类比集装箱的特点),它正是屏蔽了不同类型的数据结构之间的差异性,从而可以让数据以一种标准的形式在类之间,函数之间,对象之间进行传递,就像集装箱让产品在不同国家之间进行传递一样,那么我们就可以依据QVariant提供的标准型,在其之上建立起足够通用便捷的代码。
例如,当一个函数使用QVariant作为参数时,这恰好就提供了这样的便捷性。就拿我们上篇文章的例子setProperty,因为第二个参数value要为所有的类型提供通用转换方法,那么使用QVariant作为这些类型的存储变量,那是再合适不过了。
QObject::setProperty(const char name, const QVariant &value)
当我们想要再获取该变量时,就使用property接口,用QVariant提供的toT方法进行转换,从而得到我们想要的值。这是一个多么便捷的做法啊,我还真惊叹于实现者的思维方式。从某种意义上讲,这也可能是多态的另一种表现方式。

QVariant 类的支持的类型:

QVariant支持多种数据类型的转换,包括C++的所有基本类型,Qt提供的基本类型,甚至还可以支持自定义类型的转换

下图是其支持的类型(仅列出了一部分,详细类型可以查看帮助文档或头文件声明)
在这里插入图片描述

常用函数:

QVariant::canConvert(int targetTypeId) const

如果变量的类型可以转换为请求的类型targetTypeId,则返回true。

bool QVariant::convert(int targetTypeId)

将变量转换为请求的类型targetTypeId。如果转换不能完成,则清除变量。如果成功转换了变量的当前类型,则返回true;否则返回false。

T QVariant::value() const

返回转换为模板类型T的存储值。如果不能转换该值,将返回一个默认构造的值。

static QVariant QVariant::fromValue(const T &value)

返回一个包含值副本的QVariant。否则,其行为与setValue()完全相同。

在看下面一个例子:

QVariant 变量在类之间的传递:

#include <QVariant>
#include <QPoint>
#include <QDebug>

class MyClass
{
public:
    MyClass():i(0){}
    friend QDebug operator<<(QDebug d, const MyClass &c);
    
private:
    int i;
};
//注意,如果想要自定义的MyStruct也支持QVariant,那么需要Q_DECLARE_METATYPE向Qt元对象系统进行注册
Q_DECLARE_METATYPE(MyClass)
QDebug operator<<(QDebug d, const MyClass &c)
{
    d << "MyStruct(" << c.i << ")";
}

int main(int argc, char *argv[])
{
    QVariant v = 42;
    
    qDebug() << "v.canConvert<int>(): " << v.canConvert<int>() << v.toInt();
    qDebug() << "v.canConvert<QString>()" << v.canConvert<QString>() << v.toString();
    qDebug() << "v.canConvert<QPoint>()" << v.canConvert<QPoint>() << v.toPoint();
    
    qDebug() << "v covert to QString before:" << v;
    v.convert(QVariant::String);
    qDebug() << "v covert to QString after:" << v;
    v.convert(QVariant::Point);//如果无法转换,则QVariant本身会被清空
    qDebug() << "v covert to QPoint after:" << v;
    
    //支持自定义类型
    MyClass c;
    v.setValue(c);
    //...
    MyClass c2 = v.value<MyClass>();
    qDebug() << c2;
}

综上,QVariant可以在函数之间进行传递,同样的,我们也可以在类之间,甚至网络之间进行传递,而对象序列化也是需要QVariant这样的特性。

在今后的设计中,即使我们不使用QVariant,但是这样的思维模式是需要我们来学习的,屏蔽不同物体之间的差异,提供标准化的接口,然后依据该标准建造自己的框架体系

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值