前面我們在介紹QString的最後部分曾經提到了QByteArray這個類。現在我們就首先對這個類進行介紹。
QByteArray具有類似與QString的API。它也有相應的函數,比如left(), right(), mid()等。這些函數不僅名字和QString一樣,而且也具有幾乎相同的功能。QByteArray可以存儲原生的二進制數據和8位編碼的文本數據。這句話怎麼理解呢?我們知道,計算機內部所有的數據都是以0和1的形式存儲的。這種形式就是二進制。比如一串0、1代碼:1000,計算機並不知道它代表的是什麼,這需要由上下文決定:它可以是整數8,也可以是一個ARGB的顏色(准確的說,整數8的編碼並不是這麼簡單,但我們姑且這個理解吧)。對於文件,即便是一個文本文件,讀出時也可以按照二進制的形式讀出,這就是二進制格式。如果把這些二進制的0、1串按照編碼解釋成一個個字符,就是文本形式了。因此,QByteArray實際上是原生的二進制,但是也可以當作是文本,因此擁有文本的一些操作。但是,我們還是建議使用QString表示文本,重要的原因是,QString支持Unicode。
為了方便期間,QByteArray自動的保證“最後一個字節之後的那個位”是'\0'。這就使得QByteArray可以很容易的轉換成const char *,也就是上一章節中我們提到的那兩個函數。同樣,作為原生二進制存儲,QByteArray中間也可以存儲'\0',而不必須是'\0'在最後一位。
在有些情況下,我們希望把數據存儲在一個變量中。例如,我有一個數組,既希望存整數,又希望存浮點數,還希望存string。對於Java來說,很簡單,只要把這個數組聲明成Object[]類型的。這是什麼意思呢?實際上,這裡用到的是繼承。在Java中,int和float雖然是原生數據類型,但是它們都有分別對應一個包裝類Integer和Float。所有這些Integer、Float和String都是繼承於Object,也就是說,Integer、Float和String都是一個(也就是is-a的關系)Object,這樣,Object的數組就可以存儲不同的類型。但是,C++中沒有這樣一個Object類,原因在於,Java是單根的,而C++不是。在Java中,所有類都可以上溯到Object類,但是C++中沒有這麼一個根。那麼,怎麼實現這麼的操作呢?一種辦法是,我們都存成string類,比如int i=10,我就存"10"字符串。簡單的數據類型固然可以,可復雜一些的呢?比如一個顏色?難道要把ARGB所有的值都轉化成string?這種做法很復雜,而且失去了C++的類型檢查等好處。於是我們想另外的辦法:創建一個Object類,這是一個“很大很大的”類,裡面存儲了幾乎所有的數據類型,比如下面的代碼:
- class Object
- {
- public:
- int intValue;
- float floatValue;
- string stringValue;
- };
這個類怎麼樣?它就足以存儲int、float和string了。嗯,這就是我們的思路,也是Qt的思路。在Qt中,這樣的類就是QVariant。
QVariant可以保存很多Qt的數據類型,包括QBrush、QColor、QCursor、QDateTime、QFont、QKeySequence、QPalette、QPen、QPixmap、QPoint、QRect、QRegion、QSize和QString,並且還有C++基本類型,如int、float等。QVariant還能保存很多集合類型,如QMap<QString, QVariant>, QStringList和QList<QVariant>。item view classes,數據庫模塊和QSettings都大量使用了QVariant類,,以方便我們讀寫數據。
QVariant也可以進行嵌套存儲,例如
- QMap<QString, QVariant> pearMap;
- pearMap["Standard"] = 1.95;
- pearMap["Organic"] = 2.25;
- QMap<QString, QVariant> fruitMap;
- fruitMap["Orange"] = 2.10;
- fruitMap["Pineapple"] = 3.85;
- fruitMap["Pear"] = pearMap;
QVariant被用於構建Qt Meta-Object,因此是QtCore的一部分。當然,我們也可以在GUI模塊中使用,例如
- QIcon icon("open.png");
- QVariant variant = icon;
- // other function
- QIcon icon = variant.value<QIcon>();
我們使用了value<T>()模版函數,獲取存儲在QVariant中的數據。這種函數在非GUI數據中同樣適用,但是,在非GUI模塊中,我們通常使用toInt()這樣的一系列to...()函數,如toString()等。
如果你覺得QVariant提供的存儲數據類型太少,也可以自定義QVariant的存儲類型。被QVariant存儲的數據類型需要有一個默認的構造函數和一個拷貝構造函數。為了實現這個功能,首先必須使用Q_DECLARE_METATYPE()宏。通常會將這個宏放在類的聲明所在頭文件的下面:
- Q_DECLARE_METATYPE(BusinessCard)
然後我們就可以使用:
- BusinessCard businessCard;
- QVariant variant = QVariant::fromValue(businessCard);
- // ...
- if (variant.canConvert<BusinessCard>()) {
- BusinessCard card = variant.value<BusinessCard>();
- // ...
- }
由於VC 6的編譯器限制,這些模板函數不能使用,如果你使用這個編譯器,需要使用qVariantFromValue(), qVariantValue<T>()和qVariantCanConvert<T>()這三個宏。
如果自定義數據類型重寫了<<和>>運算符,那麼就可以直接在QDataStream中使用。不過首先需要使用qRegisterMetaTypeStreamOperators<T>().宏進行注冊。這就能夠讓QSettings使用操作符對數據進行操作,例如
- qRegisterMetaTypeStreamOperators<BusinessCard>("BusinessCard");
本文出自 “豆子空間” 博客,請務必保留此出處http://devbean.blog.51cto.com/448512/276235