我們來看一下QMetaObject的定義,我們先看一下QMetaObject對象中包含的成員資料。
struct Q_CORE_EXPORT QMetaObject
{
// ……
struct { // private data
const QMetaObject *superdata;
const char *stringdata;
const uint *data;
const void *extradata;
} d;
};
上面的代碼就是QMetaObject類所定義的全部資料成員。就是這些成員記錄了所有signal,slot,property,class information這麼多的資訊。下面讓我們來逐一解釋這些成員變數:
const QMetaObject *superdata:
這個變數指向與之對應的QObject類的父類,或者是祖先類的QMetaObject對象。
如何理解這一句話呢?我們知道,每一個QMetaObject對象,一定有一個與之相對應的QObject類(或者由其直接或間接派生出的子類),注意:這裡是類,不是對象。
那麼每一個QObject類(或其衍生類別)可能有一個父類,或者父類的父類,或者很多的繼承層次之前的祖先類。或者沒有父類(QObject)。那麼 superdata 這個變數就是指向與其最接近的祖先類中的QMetaObject對象。對於QObject類QMetaObject對象來說,這是一個NULL指標,因為QObject沒有父類。
下面,讓我們來舉例說明:
class Animal : public QObject
{
Q_OBJECT
//………….
};
class Cat : public Animal
{
Q_OBJECT
//………….
}
那麼,Cat::staticMetaObject.d.superdata 這個指標變數指向的對象是 Animal::staticMetaObject
而 Animal::staticMetaObject.d.superdata 這個指標變數指向的對象是 QObject::staticMetaObject.
而 QObject::staticMetaObject.d.superdat 這個指標變數的值為 NULL。
但如果我們把上面class的定義修改為下面的定義,就不一樣了:
class Animal : public QObject
{
// Q_OBJECT,這個 class 不定義這個
//………….
};
class Cat : public Animal
{
Q_OBJECT
//………….
}
那麼,Cat::staticMetaObject.d.superdata 這個指標變數指向的對象是 QObject::staticMetaObject
因為 Animal::staticMetaObject 這個對象是不存在的。
const char *stringdata:
顧名思義,這是一個指向string data的指標。但它和我們平時所使用的一般的字串指標卻很不一樣,我們平時使用的字串指標只是指向一個字串的指標,而這個指標卻指向的是很多個字串。那麼它不就是字串數組嗎?哈哈,也不是。因為C++的字串數組要求數組中的每一個字串擁有相同的長度,這樣才能組成一個數組。那它是不是一個字串指標數組呢?也不是,那它到底是什麼呢?讓我們來看一看它的具體值,還是讓我們以QObject這個class的QMetaObject為例來說明吧。
下面是QObject::staticMetaObject.d.stringdata指標所指向的多個字串數組,其實它就是指向一個連續的記憶體區,而這個連續的記憶體區中儲存了若干個字串。
static const char qt_meta_stringdata_QObject[] =
{
"QObject/0/0destroyed(QObject*)/0destroyed()/0"
"deleteLater()/0_q_reregisterTimers(void*)/0"
"QString/0objectName/0parent/0QObject(QObject*)/0"
"QObject()/0"
};
這個字串都是些什麼內容呀?有,Class Name, Signal Name, Slot Name, Property Name。看到這些大家是不是覺得很熟悉呀,對啦,他們就是Meta System所支援的最核心的功能屬性了。
既然他們都是不等長的字串,那麼Qt是如何來索引這些字串,以便於在需要的時候能正確的找到他們呢?第三個成員正式登場了。
const uint *data;
這個指標本質上就是指向一個正整數數組,只不過在不同的object中數組的長度都不盡相同,這取決於與之相對應的class中定義了多少signal,slot,property。
這個整數數組的的值,有一部分指出了前一個變數(stringdata)中不同字串的索引值,但是這裡有一點需要注意的是,這裡面的數值並不是直接標明了每一個字串的索引值,這個數值還需要通過一個相應的演算法計算之後,才能獲得正確的字串的索引值。
下面是QObject::staticMetaObject.d.data指標所指向的正整數數組的值。
static const uint qt_meta_data_QObject[] =
{
// content:
2, // revision
0, // classname
0, 0, // classinfo
4, 12, // methods
1, 32, // properties
0, 0, // enums/sets
2, 35, // constructors
// signals: signature, parameters, type, tag, flags
9, 8, 8, 8, 0×05,
29, 8, 8, 8, 0×25,
// slots: signature, parameters, type, tag, flags
41, 8, 8, 8, 0×0a,
55, 8, 8, 8, 0×08,
// properties: name, type, flags
90, 82, 0×0a095103,
// constructors: signature, parameters, type, tag, flags
108, 101, 8, 8, 0×0e,
126, 8, 8, 8, 0×2e,
0 // eod
};
簡單的說明一下,
第一個section,就是 //content 地區的整數值,這一塊地區在每一個QMetaObject的實體物件中數量都是相同的,含義也相同,但具體的值就不同了。專門有一個struct定義了這個section,其含義在上面的注釋中已經說的很清楚了。
struct QMetaObjectPrivate
{
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData;
};
這個 struct 就是定義第一個secton的,和上面的數值對照一下,很清晰,是吧?
第二個section,以 // signals 開頭的這段。這個section中的數值指明了QObject這個class包含了兩個signal,
第三個section,以 // slots 開頭的這段。這個section中的數值指明了QObject這個class包含了兩個slot。
第四個section,以 // properties 開頭的這段。這個section中的數值指明了QObject這個class包含有一個屬性定義。
第五個section,以 // constructors 開頭的這段,指明了QObject這個class有兩個constructor。
const void *extradata;
這是一個指向QMetaObjectExtraData資料結構的指標,關於這個指標,這裡先略過。
對於每一個具體的整數值與其所指向的實體資料之間的對應演算法,實在是有點兒麻煩,這裡就不講解細節了,有興趣的朋友自己去讀一下原始碼,一定會有很多發現。
======================================================================
聲明:
《Inside Qt Series》專欄文章是(http://www.qkevin.com)原創技術文章。
本系列專欄文章可隨意轉載,但必須保留本段聲明和每一篇文章的原始地址。
作者保留著作權,未經作者同意,不得用於任何商業用途
《Inside Qt Series》專欄文章總索引: http://www.qkevin.com/qt
本文原始地址:http://www.qkevin.com/archives/81