我们来看一下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)原创技术文章。
本系列专栏文章可随意转载,但必须保留本段声明和每一篇文章的原始地址。
作者保留版权,未经作者同意,不得用于任何商业用途