QVariant實質 (相似 C#中的裝箱拆箱)

QVariant是一種能夠存儲不一樣類型的數據結構,在不少場合這是頗有用得
爲了達到這種目的,能夠想象,該對象應該存儲對象的類型信息,數據信息以及其餘輔助詳細
考慮用途,這種對象必須支持對不一樣對象的存儲,對存儲類型的檢測以及取對象三個功能
1.對象的存儲
代碼見下:
QVariant(Type type);
QVariant(int typeOrUserType, const void *copy);
QVariant(int typeOrUserType, const void *copy, uint flags);
QVariant(const QVariant &other);

#ifndef QT_NO_DATASTREAM
QVariant(QDataStream &s);
#endif

QVariant(int i);
QVariant(uint ui);
QVariant(qlonglong ll);
QVariant(qulonglong ull);
QVariant(bool b);
QVariant(double d);
QVariant(float f) { d.is_null = false; d.type = QMetaType::Float; d.data.f =f; }
#ifndef QT_NO_CAST_FROM_ASCII
QT_ASCII_CAST_WARN_CONSTRUCTOR QVariant(const char *str);
#endif

QVariant(const QByteArray &bytearray);
QVariant(const QBitArray &bitarray);
QVariant(const QString &string);
QVariant(const QLatin1String &string);
QVariant(const QStringList &stringlist);
QVariant(const QChar &qchar);
QVariant(const QDate &date);
QVariant(const QTime &time);
QVariant(const QDateTime &datetime);
QVariant(const QList<</SPAN>QVariant> &list);
QVariant(const QMap<</SPAN>QString,QVariant> &map);
QVariant(const QHash<</SPAN>QString,QVariant> &hash);
#ifndef QT_NO_GEOM_VARIANT
QVariant(const QSize &size);
QVariant(const QSizeF &size);
QVariant(const QPoint &pt);
QVariant(const QPointF &pt);
QVariant(const QLine &line);
QVariant(const QLineF &line);
QVariant(const QRect &rect);
QVariant(const QRectF &rect);
#endif
QVariant(const QUrl &url);
QVariant(const QLocale &locale);
#ifndef QT_NO_REGEXP
QVariant(const QRegExp &regExp);
#endif
#ifndef QT_BOOTSTRAPPED
QVariant(const QEasingCurve &easing);
#endif
QVariant(Qt::GlobalColor color);
2.QVariant Type
在該對象中type負責記錄對象的類型,這對於正確取出對象是很用得
3.成員函數
Type type() const;
int userType() const;
const char *typeName() const;

bool canConvert(Type t) const;
bool convert(Type t);

#ifdef QT3_SUPPORT
inline QT3_SUPPORT bool canCast(Type t) const
{ return canConvert(t); }
inline QT3_SUPPORT bool cast(Type t)
{ return convert(t); }
#endif
這幾個函數的用途很顯然,看看其中一個的實現吧
bool QVariant::canConvert(Type t) const
{
//we can treat floats as double
//the reason for not doing it the "proper" way is that QMetaType::Float's value is 135,
//which can't be handled by qCanConvertMatrix
//In addition QVariant::Type doesn't have a Float value, so we're using QMetaType::Float
const uint currentType = ((d.type == QMetaType::Float) ? QVariant::Double : d.type);
if (uint(t) == uint(QMetaType::Float)) t = QVariant::Double;

if (currentType == uint(t))
return true;

if (currentType > QVariant::LastCoreType || t > QVariant::LastCoreType) {
switch (uint(t)) {
case QVariant::Int:
return currentType == QVariant::KeySequence
|| currentType == QMetaType::ULong
|| currentType == QMetaType::Long
|| currentType == QMetaType::UShort
|| currentType == QMetaType::UChar
|| currentType == QMetaType::Char
|| currentType == QMetaType::Short;
case QVariant::Image:
return currentType == QVariant::Pixmap || currentType == QVariant::Bitmap;
case QVariant::Pixmap:
return currentType == QVariant::Image || currentType == QVariant::Bitmap
|| currentType == QVariant::Brush;
case QVariant::Bitmap:
return currentType == QVariant::Pixmap || currentType == QVariant::Image;
case QVariant::ByteArray:
return currentType == QVariant::Color;
case QVariant::String:
return currentType == QVariant::KeySequence || currentType == QVariant::Font
|| currentType == QVariant::Color;
case QVariant::KeySequence:
return currentType == QVariant::String || currentType == QVariant::Int;
case QVariant::Font:
return currentType == QVariant::String;
case QVariant::Color:
return currentType == QVariant::String || currentType == QVariant::ByteArray
|| currentType == QVariant::Brush;
case QVariant::Brush:
return currentType == QVariant::Color || currentType == QVariant::Pixmap;
case QMetaType::Long:
case QMetaType::Char:
case QMetaType::UChar:
case QMetaType::ULong:
case QMetaType::Short:
case QMetaType::UShort:
return qCanConvertMatrix[QVariant::Int] & (1 << currentType) || currentType== QVariant::Int;
default:
return false;
}
}

if(t == String && currentType == StringList)
return v_cast<</SPAN>QStringList>(&d)->count() == 1;
else
return qCanConvertMatrix[t] & (1 << currentType);
}
該函數做用是檢測存儲對象是否能夠轉換爲輸入類型,具體實現很明瞭
4.QVariant對象的最後一個主要的功能就是到給定類型的轉換
函數簇以下:
int toInt(bool *ok = 0) const;
uint toUInt(bool *ok = 0) const;
qlonglong toLongLong(bool *ok = 0) const;
qulonglong toULongLong(bool *ok = 0) const;
bool toBool() const;
double toDouble(bool *ok = 0) const;
float toFloat(bool *ok = 0) const;
qreal toReal(bool *ok = 0) const;
QByteArray toByteArray() const;
QBitArray toBitArray() const;
QString toString() const;
QStringList toStringList() const;
QChar toChar() const;
QDate toDate() const;
QTime toTime() const;
QDateTime toDateTime() const;
QList<</SPAN>QVariant> toList() const;
QMap<</SPAN>QString, QVariant> toMap() const;
QHash<</SPAN>QString, QVariant> toHash() const;
其一個實現以下:

QTime QVariant::toTime() const
{
return qVariantToHelper<</SPAN>QTime>(d, Time, handler);
}
使用了模板函數:qVariantToHelper
5.關於qVariantToHelper


template <</SPAN>typename T>
inline T qVariantToHelper(const QVariant::Private &d, QVariant::Type t,
const QVariant::Handler *handler, T * = 0)
{
if (d.type == t)
return *v_cast<</SPAN>T>(&d);

T ret;
handler->convert(&d, t, &ret, 0);
return ret;
}
該函數根據對象信息和目標類型作轉換工做,若是兩者類型一致,則直接作轉換,不然交給函數handler->convert處理
6.關於Handler對象
struct Handler {
f_construct construct;
f_clear clear;
f_null isNull;
#ifndef QT_NO_DATASTREAM
f_load load;
f_save save;
#endif
f_compare compare;
f_convert convert;
f_canConvert canConvert;
f_debugStream debugStream;
};
不過好像沒看出什麼門道,那就繼續看看
其實際結構爲:
const QVariant::Handler qt_kernel_variant_handler = {
construct,
clear,
isNull,
#ifndef QT_NO_DATASTREAM
0,
0,
#endif
compare,
convert,
0,
#if !defined(QT_NO_DEBUG_STREAM) && !defined(Q_BROKEN_DEBUG_STREAM)
streamDebug
#else
0
#endif
};
再看其中 一個函數實現constuct
static void construct(QVariant::Private *x, const void *copy)
{
x->is_shared = false;

switch (x->type) {
case QVariant::String:
v_construct<</SPAN>QString>(x, copy);
break;
case QVariant::Char:
v_construct<</SPAN>QChar>(x, copy);
break;
case QVariant::StringList:
v_construct<</SPAN>QStringList>(x, copy);
break;
case QVariant::Map:
v_construct<</SPAN>QVariantMap>(x, copy);
break;
case QVariant::Hash:
v_construct<</SPAN>QVariantHash>(x, copy);
break;
case QVariant::List:
v_construct<</SPAN>QVariantList>(x, copy);
break;
case QVariant::Date:
v_construct<</SPAN>QDate>(x, copy);
break;
case QVariant::Time:
v_construct<</SPAN>QTime>(x, copy);
break;
case QVariant::DateTime:
v_construct<</SPAN>QDateTime>(x, copy);
break;
case QVariant::ByteArray:
v_construct<</SPAN>QByteArray>(x, copy);
break;
case QVariant::BitArray:
v_construct<</SPAN>QBitArray>(x, copy);
break;
#ifndef QT_NO_GEOM_VARIANT
case QVariant::Size:
v_construct<</SPAN>QSize>(x, copy);
break;
case QVariant::SizeF:
v_construct<</SPAN>QSizeF>(x, copy);
break;
case QVariant::Rect:
v_construct<</SPAN>QRect>(x, copy);
break;
case QVariant::LineF:
v_construct<</SPAN>QLineF>(x, copy);
break;
case QVariant::Line:
v_construct<</SPAN>QLine>(x, copy);
break;
case QVariant::RectF:
v_construct<</SPAN>QRectF>(x, copy);
break;
case QVariant::Point:
v_construct<</SPAN>QPoint>(x, copy);
break;
case QVariant::PointF:
v_construct<</SPAN>QPointF>(x, copy);
break;
#endif
case QVariant::Url:
v_construct<</SPAN>QUrl>(x, copy);
break;
case QVariant::Locale:
v_construct<</SPAN>QLocale>(x, copy);
break;
#ifndef QT_NO_REGEXP
case QVariant::RegExp:
v_construct<</SPAN>QRegExp>(x, copy);
break;
#endif
#ifndef QT_BOOTSTRAPPED
case QVariant::EasingCurve:
v_construct<</SPAN>QEasingCurve>(x, copy);
break;
#endif
case QVariant::Int:
x->data.i = copy ? *static_cast<</SPAN>const int *>(copy) : 0;
break;
case QVariant::UInt:
x->data.u = copy ? *static_cast<</SPAN>const uint *>(copy) : 0u;
break;
case QVariant::Bool:
x->data.b = copy ? *static_cast<</SPAN>const bool *>(copy) : false;
break;
case QVariant::Double:
x->data.d = copy ? *static_cast<</SPAN>const double*>(copy) : 0.0;
break;
case QMetaType::Float:
x->data.f = copy ? *static_cast<</SPAN>const float*>(copy) : 0.0f;
break;
case QMetaType::QObjectStar:
x->data.o = copy ? *static_cast<</SPAN>QObject *const*>(copy) : 0;
break;
case QVariant::LongLong:
x->data.ll = copy ? *static_cast<</SPAN>const qlonglong *>(copy) : Q_INT64_C(0);
break;
case QVariant::ULongLong:
x->data.ull = copy ? *static_cast<</SPAN>const qulonglong *>(copy) : Q_UINT64_C(0);
break;
case QVariant::Invalid:
case QVariant::UserType:
break;
default:
void *ptr = QMetaType::construct(x->type, copy);
if (!ptr) {
x->type = QVariant::Invalid;
} else {
x->is_shared = true;
x->data.shared = new QVariant::PrivateShared(ptr);
}
break;
}
x->is_null = !copy;
}
繼續看v_construct
該函數模板實現和平臺有關,其中一個平臺的實現以下:
template <</SPAN>class T>
inline void v_construct(QVariant::Private *x, const void *copy, T * = 0)
{
if (sizeof(T) > sizeof(QVariant::Private::Data)) {
x->data.shared = copy ? new QVariantPrivateSharedEx<</SPAN>T>(*static_cast<</SPAN>const T *>(copy))
: new QVariantPrivateSharedEx<</SPAN>T>;
x->is_shared = true;
} else {
if (copy)
new (&x->data.ptr) T(*static_cast<</SPAN>const T *>(copy));
else
new (&x->data.ptr) T;
}
}
這裏主要是把傳入對象指針導入爲自己的數據指針data.ptr

QVariant大體就這個樣子
http://blog.sina.com.cn/s/blog_a401a1ea0101i8k9.html
 
--------------------------------------------------------------------------------------------------------------------

有時須要用同一個變量來保存不一樣類型的數據。一種方法是將數據編碼爲QByteArray QString。如串既可保存文本也可保存數字。Qt提供了一種更好的方式使用QVariant.html

 

QVariant類能夠保存許多Qt類型的值,包括QBrushQColorQCursorQDateTimeQFont,QKeySequenceQPaletteQPenQPixmapQPointQRectQRegionQSizeQString,也包括基本的C++數字類型,如double int.還可保存容器如QMapQStringList QList數據結構

使用QVariant經過嵌套容器類型值,能夠建立任意複雜的數據結構:app

 

QMap pearMap;函數

pearMap["Standard"] = 1.95;oop

pearMap["Organic"] = 2.25;ui

QMap fruitMap;編碼

fruitMap["Orange"] = 2.10;url

fruitMap["Pineapple"] = 3.85;spa

fruitMap["Pear"] = pearMap;debug

 

此處咱們建立了一個map,擁有string鍵(產品名),值要麼是浮點數(價格)或要麼是map。頂層map有三個鍵:"Orange", "Pear", 和 "Pineapple"。與"Pear"鍵相對應的值是一個map,含有兩個鍵("Standard" and "Organic").當遍歷保存可變值的map時,須要用type()來檢查類型。

 

建立這樣的數據結構看起來很好,但QVariant以犧牲效率和可讀性爲代價。因此一般定義一個恰當的C++類來保存數據。

 

QIcon icon("open.png");

QVariant variant = icon;

 

爲得到來自QVariant的GUI-related類型的值,可以使用QVariant::value()模板函數:

QIcon icon = variant.value();

value()函數也用於非GUI數據類型與QVariant之間的轉換,但實際上咱們常常使用to...()轉換函數(如,toString())。

QVariant也能用於保存自定義的數據類型,假設這些類型能提供一個默認構造函數和拷貝構造函數。所以,首先要用Q_DECLARE_METATYPE()宏註冊該類型,特別是在頭文件中類定義的下面:

 

Q_DECLARE_METATYPE(BusinessCard)

代碼以下:

BusinessCard businessCard;

QVariant variant = QVariant::fromValue(businessCard);

...

if (variant.canConvert()) {

BusinessCard card = variant.value();

...

}

 

因受編譯器的限制,MSVC 6不能得到這些模板函數。若是想用這種編譯器,使用qVariantFromValue(), qVariantValue(), 和 qVariantCanConvert()全局函數。

若是自定義數據類型有<< 和 >>運算符用於QDataStream的寫和讀,可使用qRegisterMetaTypeStreamOperators()註冊這些運算符。這樣就可使用QSettings來保存自定義類型的參數。如:

qRegisterMetaTypeStreamOperators("BusinessCard");

 

Qt還提供其它的一些容器。QPair只保存兩個值,相似於std::pair. QBitArray會在第十九章中使用。QVarLengthArray是一個對QVector的低級替代,由於它在棧上分配內存不能implicitly shared,它的overhead比QVector的少,因此它更適合緊循環(tight loops)。
 
http://blog.sina.com.cn/s/blog_a401a1ea0101f8nv.html
相關文章
相關標籤/搜索