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