Qt 核心机制解析(一、MOC机制)
C++扩展语法
Qt对C++进行了一些扩展,主要是三个方面:
- 元对象系统,包含
Q_OBJECT
宏的文件,需要 moc 预处理。moc 把qt扩展的C++语法翻译为标准C++代码 - 资源系统,
.qrc
文件需要 rcc 进行预处理 - 界面系统,
.ui
文件需要 uic 进行预处理
要使用这些扩展机制,比较常用的方法是通过 qmake 或 cmake 生成 makefile,然后再生成可执行文件。在使用 cmake 构建项目时,需要加上以下语句来表明需要使用Qt的扩展机制:
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
我们主要研究的是MOC机制,Qt的核心机制也是通过 MOC 来实现的,Qt 相对于标准 C++增添的特性主要有以下体现:
- 支持对象间通信信号与槽机制
- 支持可查询和可设计的动态对象属性机制
- 事件和事件过滤器
- 国际化支持
- 支持多任务的定时器
- 支持按层检索的对象树
- 受保护指针
- 动态类型转换
MOC
Qt 程序在交由标准编译器编译之前,先要使用 moc 分析 C++ 源文件。如果它发现在一个头文件中包含了宏 Q_OBJECT
,则会生成另外一个 C++ 源文件。这个源文件中包含了 Q_OBJECT 宏的实现代码。这个新的文件名字将会是原文件名前面加上 moc_ 构成。这个新的文件同样将进入编译系统,最终被链接到二进制代码中去。因此我们可以知道,这个新的文件不是“替换”掉旧的文件,而是与原文件一起参与编译。
接下来,以一个简单的Q_OBJECT
为例,我们来分析元对象编译器生成了什么
// mywidget.cpp
#include <QObject>
class MyWidget : public QObject
{
Q_OBJECT
public:
explicit MyWidget(QObject *parent = nullptr);
signals:
void signal_test(int index);
public slots:
void slot_test(int index) {};
};
我们查看Q_OBJECT
的定义,它会被展开成以下内容:
public:
QT_WARNING_PUSH
Q_OBJECT_NO_OVERRIDE_WARNING
// 定义了一个静态的 QMetaObject 对象。这个对象包含了元对象的描述信息,用于支持信号和槽机制、反射等功能
static const QMetaObject staticMetaObject;
// 返回当前的 QMetaObject
virtual const QMetaObject *metaObject() const;
// 进行元对象类型的转换
virtual void *qt_metacast(const char *);
// 通过该函数调用槽函数,实际是调用了 qt_static_metacall
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
扩展后带来的一些变量和函数的定义,而这些定义都已经被写入到了moc_mywidget.cpp
文件中,因此,在编译时mywidget.cpp
会与moc_mywidget.cpp
一起编译,否则这个类是不完整的。
在后续,我们会结合生成的moc_mywidget.cpp
的内容,讨论由MOC生成的函数的作用