關於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,但是這樣的思維模式是需要我們來學習的,屏蔽不同物體之間的差異,提供標準化的接口,然後依據該標準建造自己的框架體系

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章