Qt中的元对象系统(Meta-Object System)提供了内部对象间通讯的信号和槽机制、运行时类型信息和动态属性系统。
整个元对象系统基于三个东西建立:
moc 全称是 Meta-Object Compiler(元对象编译器),它是一个工具(类似于 qmake), 该工具读取并分析 C++源文件,若发现一个或多个包含了 Q_OBJECT 宏的类的声明, 则会生成另外一个包含了 Q_OBJECT 宏实现代码的 C++源文件(该源文件通常名称为moc_*.cpp
) ,这个新的源文件要么被#include 包含到类的源文件中,要么被编译键接到类的实现中(通常是使用的此种方法)。注意:新文件不会“替换”掉旧的文件, 而是与原文件一起编译。
元对象系统主要是为了实现信号和槽机制才被引人的,不过除了信号和槽机制以外,元对象系统还提供了其他一些特性,属性系统,动态运行时信息:
QObject : : metaObject()函数可以返回一个类的元对象;
QMetaObject : :className()可以在运行时以字符串形式返回类名,而不需要C++编辑器原生的运行时类型信息(RTTI)的支持;
QObject : : inherits()函数返回一个对象是否是QObject继承树上一个类的实例的信息;
QObject : : tr()和 QObject : : trUtf8()进行字符串翻译来实现国际化;
QObject : : setProperty()和 QObject : : property()通过名字来动态设置或者获取对象属性;
QMetaObject : : newInstance()构造类的一个新实例。
除了这些特性,还可以使用qobject_cast(QObject *object)函数对QObject类进行动态类型转换,这个函数的功能类似于标准C++中的dynamic_cast()函数,但它不再需要RTTI的支持。这个函数尝试将它的参数转换为尖括号中的类型的指针,如果是正确的类型,则返回一个非零的指针:如果类型不兼容,则返回0。例如:
QObject *obj = new QTimer; // QTimer inherits QObject
QTimer *timer = qobject_cast(obj);
// timer == (QObject *)obj
QAbstractButton *button = qobject_cast(obj);
// button == 0>(obj):
/* qmake ignore Q_OBJECT */
#define Q_OBJECT \
public: \
QT_WARNING_PUSH \
Q_OBJECT_NO_OVERRIDE_WARNING \
static const QMetaObject staticMetaObject; \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
QT_TR_FUNCTIONS \
private: \
Q_OBJECT_NO_ATTRIBUTES_WARNING \
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
QT_WARNING_POP \
struct QPrivateSignal {}; \
QT_ANNOTATE_CLASS(qt_qobject, "")
可见,Q_OBJECT 宏为声明的类之中增加了一些成员,而且还有虚函数成员(注意,这些虚函数没有定义),按 C++语法,虚函数必须被定义或者被声明为纯虚函数,moc 工具的工作之一就是生成以上成员的定义,并且还会生成一些其他必要的代码。此处讲解 Q_OBJECT 宏代码的目的主要是为了讲解使用 Qt Creator 时易犯的错误及怎样正确使用 Q_OBJECT 宏,读者可查看由 moc 生成的 moc_*.cpp
的内容以了解更详细的信息。
1、此时 moc 工具是通过 Qt Creator 来使用的,因此必须保证 moc 能发现并处理项目中包含有 Q_OBJECT 宏的类,为此,需要遵守以下规则
2、不按规则 1 的方法编写程序,则 moc 工具就不能正确生成代码,这时的错误原因通常是未定义由 Q_OBJECT 展开后在类中声明的虚函数引起的,其错误信息如下:
undefined reference to 'vtable for A'
.表示类 A 的虚函数表(vtable)不能正常生成,通常是有虚函数未定义。"public: virtual struct QMetaObject const * thiscall A::metaObject(void)const " (……)
.表示虚函数 A::metaObject 未定义。3、以下错误不易被发现:
若定义了 QObject 类的派生类,并进行了构建,在这之后再添加 Q_OBJECT 宏,则会编译出错。解决方案:此时必须执行一次 qmake 命令(“构建”>“执行 qmake”),否则 moc 不能生成代码。
示例:使用 Qt Creator 启动元对象系统
新建空项目、添加一个 m.h 和 m.cpp 文件。
/*m.h 文件内容。要在 Qt Creator 中启动元对象系统,包含 Q_OBJECT 宏的类的定义必须位于头文件中,否则 moc 工具不能生成元对象代码*/
#ifndef M_H //用于防止头文件被多次包含的逻辑指令#define M_H
#include //因为要使用 QObject 类,为此需要包含此头文件class B{};
//错误,moc 不能启动。因为多重继承时 QObject 必须位于基类列表中的第一位。
//class A:public B,public QObject{ Q_OBJECT };
class B
{
};
class A : public QObject, public B
{ /*要使用元对象系统必须继承自 QObject 类,且 QObject 应位于基类继承列表中的第一位。*/
Q_OBJECT //启动元对象系统。Q_OBJECT 必须位于私有区域。
public : A()
{
qDebug("DDDD\n");
}
}; //qDebug 函数用于在控制台输出一些信息
#endif // M_H
//m.cpp 文件内容。
#include "m.h"
int main(int argc, char *argv[])
{
A ma;
return 0;
}
运行以上程序,可在 debug 目录下找到一个 moc_m.cpp 的源文件,该源文件就是使用 moc 工具生成的,该源文件中的代码就是元对象代码,读者可查看其代码。若在该目录没有 moc_m.cpp 文件,说明 moc 工具未能正常启动,这时需在 Qt Creator 中执行 qmake 命令, 再构建程序。
错误的运行
正确的运行
参考:
QT核心机制1:元系统_lczdk的博客-CSDN博客
Qt笔记 元对象系统 - 橘崽崽啊 - 博客园