- 继承自 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 程序对该宏的处理
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 中。
既然 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 文件出来看看
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 数组,这个数组中存放的是一些索引值,来指导我们从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,我不清楚该宏起什么作用的。
整理思路太累了,写完这个,还不清楚什么时候再写(二)
参考以上是关于Qt Meta Object system 学习的主要内容,如果未能解决你的问题,请参考以下文章
深入了解Qt之元对象系统(Meta-Object System)