Qt5是一个跨平台C++框架,它有个突出的特点就是其元对象系统,该系统通过扩展C++的能力,为事件处理提供了信号与槽机制、为对象内省提供了属性系统。为了支持这些特性,Qt引入了元对象编译器(Meta-Object Compiler, MOC),这用于解析C++头文件并生成附加的源代码,并与其他代码一起编译,实现元对象系统的功能。
Qt的元对象编译器是Qt中相对复杂的一个部分,因此本文深入moc的技术细节,为你揭开Qt元对象系统神秘的面纱。
moc的工作原理
moc会读取C++源文件,寻找Qt特定的宏,如Q_OBJECT
、signals
、slots
和Q_PROPERTY
。当它发现这些宏时,它会生成一个C++源文件,其中包含了类的元信息,然后将这个文件以合适的方式编译和链接到应用程序中。具体的链接细节可以参考文章《在Qt中,直接include <moc_xxxxx.cpp> 为什么不会出现符号冲突的错误?》。
生成的代码信息比较多,具体来说,moc生成的代码包括:
- 元对象代码,提供了关于对象的信息,例如其类名、超类名、方法、属性和信号/槽。
- 信号和槽的实现,使得信号-槽连接机制成为可能,允许对象之间进行松耦合的通信。
- 动态属性系统代码,允许在运行时内省和修改对象属性。
这些生成代码一般在Build目录下/XXX/XXX_autogen,例如:
moc示例与代码
我们可以通过一个简单的例子来观察moc的原理。
假设我们有一个MyObject.h
头文件,其中定义了一个类:
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
class MyObject : public QObject {
Q_OBJECT
Q_PROPERTY(int myProperty READ myProperty WRITE setMyProperty NOTIFY myPropertyChanged)
public:
MyObject() : m_myProperty(0) {
}
int myProperty() const {
return m_myProperty; }
void setMyProperty(int value) {
if (value != m_myProperty) {
m_myProperty = value;
emit myPropertyChanged(value);
}
}
signals:
void myPropertyChanged(int newValue);
private:
int m_myProperty;
};
#endif // MYOBJECT_H
在这个类中,我们有一个属性myProperty
以及对应的getter和setter方法,还有一个在属性改变时会发射的信号myPropertyChanged
。为了让这个类可以使用Qt的元对象系统,类定义中包含了Q_OBJECT
宏。
当你编译时,构建系统会先调用qmake.exe,对源码进行扫描。这个步骤以CMake生成的VisualStudio为例,在PreBuild阶段:
moc会生成一个源文件(通常命名为moc_MyObject.cpp
),它包含了MyObject
的元对象代码。生成代码的简化节选如下所示:
// moc_MyObject.cpp
#include "MyObject.h"
// MyObject的元对象代码
static const QtMetaObject staticMetaObject = {
{
&QObject::staticMetaObject, qt_meta_stringdata_MyObject,
qt_meta_data_MyObject, qt_static_metacall, nullptr, nullptr }
};
void MyObject::qt_static_metacall(QObject *_obj, QMetaObject::Call _c, int _id, void **_a) {
if (_c == QMetaObject::InvokeMetaMethod) {
MyObject *_t = static_cast<MyObject *>(_obj);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->myPropertyChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
default: ;
}
}
}
const QMetaObject *MyObject::metaObject() const {
return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
}
// 还会生成信号和属性系统的实现代码...
这些都是元对象系统工作所需的生成的代码,包括类的元对象信息,静态调用函数用于调用方法和访问属性,以及其他必要的函数。
完整的代码如下:
/****************************************************************************
** Meta object code from reading C++ file 'MyObject.cpp'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.15.16)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
#include <memory>
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'MyObject.cpp' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.15.16. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#