Qt Meta Object system 学习

Posted ilvu999

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt Meta Object system 学习相关的知识,希望对你有一定的参考价值。

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,我不清楚该宏起什么作用的。

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

参考

以上是关于Qt Meta Object system 学习的主要内容,如果未能解决你的问题,请参考以下文章

深入了解Qt之元对象系统(Meta-Object System)

Qt MetaObject System详解

Qt:meta.enumeratorCount() 没有元数据用于 Q_OBJECT 中的枚举,为啥?

Qt元对象系统简介

Qt Meta Type System

Qt元对象(Meta-Object)系统与反射