您的位置:首页 > 移动开发 > Objective-C

Qt Meta Object system 学习(一)

2012-10-08 20:49 267 查看

Qt Meta Object system 学习(一)

使用 meta object system

继承自 QOject
类定义中添加 Q_OBJECT 宏
使用 moc 程序对包含该宏的文件进行处理

采用 qmake 进行处理时,如果头文件xxx.h内包含 Q_OBJECT 宏,将生成 moc_xxx.cpp 文件。如果xxx.cpp文件内包含宏,将生成 xxx.moc 文件(这时,我们需要在xxx.cpp文件内添加 #include"xxx.moc")

Q_OBJECT宏
包括两个方面,

该宏在C++中的展开,有编译预处理器完成
moc 程序对该宏的处理

宏定义#define Q_OBJECT \

public: \

Q_OBJECT_CHECK \

static const QMetaObject staticMetaObject; \

virtual const QMetaObject *metaObject() const; \

virtual void *qt_metacast(const char *); \

QT_TR_FUNCTIONS \

virtual int qt_metacall(QMetaObject::Call, int, void **); \

private:

而宏 QT_TR_FUNCTIONS 将展开为我们使程序国际化经常使用的 tr 与 trUtf8函数

# define QT_TR_FUNCTIONS \

static inline QString tr(const char *s, const char *c = 0) \

{ return staticMetaObject.tr(s, c); } \

static inline QString trUtf8(const char *s, const char *c = 0) \

{ return staticMetaObject.trUtf8(s, c); } \

static inline QString tr(const char *s, const char *c, int n) \

{ return staticMetaObject.tr(s, c, n); } \

static inline QString trUtf8(const char *s, const char *c, int n) \

{ return staticMetaObject.trUtf8(s, c, n); }

moc 处理
Q_OBJECT 为我们添加了这么多成员函数,而这些函数我们有没有自己去实现,那么其定义在哪儿呢? 这就是 moc 为我们做的,自动生成这些成员函数的定义,放于生成的 xxx.moc 或 moc_xxx.cpp 文件内

注意:两个文件的区别(如果你用cmake或其他工具的话,这点可能很重要)

生成的 moc_xxx.cpp 中会自动包含 xxx.h 头文件,所以它可以被独立编译成目标文件(.o 或 .obj)
生成的 xxx.moc 是不会包含 xxx.cpp 的(要是包含就出问题了,能发现吧?),因此 xxx.moc 中没有类定义,它无法被独立编译,只能被 include 到 xxx.cpp 中。

QMetaObject
既然 Q_OBJECT 展开成与 QMetaObject 有关的成员函数,看一下QMetaObject 都提供哪些常用功能

className() 返回类的名字
superClass() 返回父类的 QMetaObject 对象
method()与methodCount() 提供meta函数信息(包括signals, slots 与 invokable函数)
enumerator()与 enumeratorCount() 提供enum类型信息
propertyCount()与 property() 提供属性信息
constructor()与 constructorCount() 提供 meta-constructors 信息

既然meta object能为我们的类提供这么多信息,那么这些信息存放哪儿了(大家肯定都能猜到秘密在moc生成的文件内,但为清楚起见,我们看一下QMetaObject的定义)。

这是用 struct 定义的一个类,我们略过其它,只看其数据成员

struct QMetaObject

{

...

...

private:

struct { // private data

const QMetaObject *superdata;

const char *stringdata;

const uint *data;

const void *extradata;

} d;

}
其中呢,

uperdata,指向父类的MetaObject,容易理解

extradata 似乎目前未启用,不用理解
剩下两个是什么呢? 如你所想,就在 moc 生成的文件内

moc生成的文件
随便找个 moc 文件出来看看

static const uint qt_meta_data_HPixmapScene[] = {...};

static const char qt_meta_stringdata_HPixmapScene[] = {...};

const QMetaObject HPixmapScene::staticMetaObject = {

{ &QGraphicsScene::staticMetaObject, qt_meta_stringdata_HPixmapScene,

qt_meta_data_HPixmapScene, 0 }

};
这是一个QGraphicsScene的子类。对比前面QMetaObject的数据成员看看,是不是很简单:

先分别定义1个 uint 和 char 的数组,

用这两个数组首地址和父类的MetaObject的指针初始化 staticMetaObject

这个 staticMetaObject 是我们自己的类的静态成员变量

uint数组
接下来我们可以看看 uint 数组,这个数组中存放的是一些索引值,来指导我们从char字符串中提取信息

static const uint qt_meta_data_HPixmapScene[] = {

// content:

4, // revision

0, // classname

0, 0, // classinfo

2, 14, // methods

0, 0, // properties

0, 0, // enums/sets

0, 0, // constructors

0, // flags

0, // signalCount

// slots: signature, parameters, type, tag, flags

18, 14, 13, 13, 0x0a,

41, 37, 13, 13, 0x0a,

0 // eod

};
对比QMetaObject用到的数据结构 QMetaObjectPrivate,看看,是不是豁然开朗了:uint 数组中的第一段 就对应这个结构体

struct QMetaObjectPrivate

{

int revision;

int className;

int classInfoCount, classInfoData;

int methodCount, methodData;

int propertyCount, propertyData;

int enumeratorCount, enumeratorData;

int constructorCount, constructorData; //since revision 2

int flags; //since revision 3

int signalCount; //since revision 4

}char 数组
配合上面的索引数组,继续看一下:

static const char qt_meta_stringdata_HPixmapScene[] = {

"HPixmapScene\0\0pix\0setPixmap(QPixmap)\0"

"img\0setImage(QImage)\0"

};
索引数组中 className 为0,我们看字符数组中从0开始是什么?恩,就是我们类的名字。

我们类的类中还定义的两个slot函数

public slots:

void setPixmap(const QPixmap& pix);

void setImage(const QImage& img);
我们看看是怎么对应的,

首先看索引数组 methodCount=2, methodData=14,告诉我们有两个method,数据从索引数组的14开始

然后,我们从索引数组的14开始看,{18, 14, 13, 13, 0x0a

这儿,18和14对应到我们的char数组了,看一下,分别是setPixmap(QPixmap) 和 pix。怎么样,和我们定义的函数对应上了吧。

有什么不对

其实上面有一处不太对,从开始就不太对。什么不对呢?

Qt manual 对此只提了一句,在QObject的manual中,除此之外,似乎再没出现过:要用 meta object system,我们不一定要继承QObject,普通的C++的类也可以部分使用,与此对应,我们不用Q_OBJECT宏,而用Q_GADGET宏,当然,这个只提供部分meta object system 的功能,比如Qt
元对象系统之 "enum自省"

另外:ActiveQt模块中,QAxObject和QAxWidget 定义了信号槽,却没有使用Q_OBJECT,具体可以看
ActiveQt模块学习(三)

另外 moc 生成的文件内有一个 Q_NO_DATA_RELOCATION 宏,无论用 Q_OBJECT 还是 Q_GADGET,我不清楚该宏起什么作用的。

整理思路太累了,写完这个,还不清楚什么时候再写(二)

参考

http://doc.qt.nokia.com/latest/object.html

http://doc.qt.nokia.com/latest/metaobjects.html

Qt MetaObject System详解之一:简介

Inside Qt Series (五):元对象系统(Meta-Object System)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: