采用 qmake 进行处理时,如果头文件xxx.h内包含 Q_OBJECT 宏,将生成 moc_xxx.cpp 文件。如果xxx.cpp文件内包含宏,将生成 xxx.moc 文件(这时,我们需要在xxx.cpp文件内添加 #include"xxx.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); }
Q_OBJECT 为我们添加了这么多成员函数,而这些函数我们有没有自己去实现,那么其定义在哪儿呢? 这就是 moc 为我们做的,自动生成这些成员函数的定义,放于生成的 xxx.moc 或 moc_xxx.cpp 文件内
注意:两个文件的区别(如果你用cmake或其他工具的话,这点可能很重要)
既然 Q_OBJECT 展开成与 QMetaObject 有关的成员函数,看一下QMetaObject 都提供哪些常用功能
既然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,容易理解
随便找个 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的数据成员看看,是不是很简单:
用这两个数组首地址和父类的MetaObject的指针初始化 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
}
配合上面的索引数组,继续看一下:
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);
我们看看是怎么对应的,
然后,我们从索引数组的14开始看,{18, 14, 13, 13, 0x0a
Qt manual 对此只提了一句,在QObject的manual中,除此之外,似乎再没出现过:要用 meta object system,我们不一定要继承QObject,普通的C++的类也可以,与此对应,我们不用Q_OBJECT宏,而用Q_GADGET宏,当然,这个只提供部分meta object system 的功能,比如 Qt 元对象系统之 "enum自省"
另外 moc 生成的文件内有一个 Q_NO_DATA_RELOCATION 宏,无论用 Q_OBJECT 还是 Q_GADGET,我不清楚该宏起什么作用的。
整理思路太累了,写完这个,还不清楚什么时候再写(二)
联系客服