首先,给定一个示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
class MyClass : public QObject
{
Q_OBJECT // 这个宏告诉MOC这个类要使用元对象系统
public:
MyClass(QObject *parent = nullptr);
signals:
void mySignal(int value); // 定义一个带有一个int参数的信号
public slots:
void mySlot(int value); // 定义一个带有一个int参数的槽
};
Q_OBJECT 这个宏是Qt元对象的核心,查看它的展开如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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, "")
相当于对于需要Qt信号槽机制的类,比如注册Qt的元对象(即Q_OBJECT),然后Q_OBJECT为这个类添加了一些列元对象结构和相应的函数接口,比如上面的核心staticMetaObject、qt_static_metacall .
查看该头文件编译后的moc_xx.cpp:
moc_xx.cpp源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
QT_BEGIN_MOC_NAMESPACE QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED struct qt_meta_stringdata_MyWidget_t { QByteArrayData data[4]; char stringdata0[26]; }; #define QT_MOC_LITERAL(idx, ofs, len) \ Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ qptrdiff(offsetof(qt_meta_stringdata_MyWidget_t, stringdata0) + ofs \ - idx * sizeof(QByteArrayData)) \ ) static const qt_meta_stringdata_MyWidget_t qt_meta_stringdata_MyWidget = { { QT_MOC_LITERAL(0, 0, 8), // "MyWidget" QT_MOC_LITERAL(1, 9, 8), // "mySignal" QT_MOC_LITERAL(2, 18, 0), // "" QT_MOC_LITERAL(3, 19, 6) // "mySlot" }, "MyWidget\0mySignal\0\0mySlot" }; #undef QT_MOC_LITERAL static const uint qt_meta_data_MyWidget[] = { // content: 8, // revision 0, // classname 0, 0, // classinfo 2, 14, // methods 0, 0, // properties 0, 0, // enums/sets 0, 0, // constructors 0, // flags 1, // signalCount // signals: name, argc, parameters, tag, flags 1, 0, 24, 2, 0x06 /* Public */, // slots: name, argc, parameters, tag, flags 3, 0, 25, 2, 0x0a /* Public */, // signals: parameters QMetaType::Void, // slots: parameters QMetaType::Void, 0 // eod }; void MyWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) { if (_c == QMetaObject::InvokeMetaMethod) { auto *_t = static_cast<MyWidget *>(_o); Q_UNUSED(_t) switch (_id) { case 0: _t->mySignal(); break; case 1: _t->mySlot(); break; default: ; } } else if (_c == QMetaObject::IndexOfMethod) { int *result = reinterpret_cast<int *>(_a[0]); { using _t = void (MyWidget::*)(); if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MyWidget::mySignal)) { *result = 0; return; } } } Q_UNUSED(_a); } QT_INIT_METAOBJECT const QMetaObject MyWidget::staticMetaObject = { { &QWidget::staticMetaObject, qt_meta_stringdata_MyWidget.data, qt_meta_data_MyWidget, qt_static_metacall, nullptr, nullptr } }; const QMetaObject *MyWidget::metaObject() const { return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; } void *MyWidget::qt_metacast(const char *_clname) { if (!_clname) return nullptr; if (!strcmp(_clname, qt_meta_stringdata_MyWidget.stringdata0)) return static_cast<void*>(this); return QWidget::qt_metacast(_clname); } int MyWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a) { _id = QWidget::qt_metacall(_c, _id, _a); if (_id < 0) return _id; if (_c == QMetaObject::InvokeMetaMethod) { if (_id < 2) qt_static_metacall(this, _c, _id, _a); _id -= 2; } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { if (_id < 2) *reinterpret_cast<int*>(_a[0]) = -1; _id -= 2; } return _id; } // SIGNAL 0 void MyWidget::mySignal() { QMetaObject::activate(this, &staticMetaObject, 0, nullptr); } QT_WARNING_POP QT_END_MOC_NAMESPACE
结合其中Qt生成的注释来理解:Moc编译器创建了一个包含信号和槽信息的元对象。这个元对象是MyWidget类的一部分,并包含了类名、信号和槽的名称、信号和槽的参数类型等信息。
元对象的结构
元对象的定义部分看起来是这样的:
1
2
3
4
5
6
7
8
QT_INIT_METAOBJECT const QMetaObject MyWidget::staticMetaObject = { {
&QWidget::staticMetaObject, // 父类的元对象
qt_meta_stringdata_MyWidget.data, // 类名、信号和槽的名称
qt_meta_data_MyWidget, // 其他元数据,包括信号和槽的参数类型
qt_static_metacall, // 用于调用信号和槽的函数
nullptr,// 属性列表
nullptr// 枚举列表
} };
可以看到,元对象包含了一些元数据,如类名、信号和槽的名称等。这些信息是在编译时由Moc编译器生成的,并存储在qt_meta_stringdata_MyWidget和qt_meta_data_MyWidget这两个静态变量中。
信号和槽的调用
元对象还包含一个qt_static_metacall函数,这个函数是用来调用信号和槽的。这个函数会接收一个指向QObject的指针、一个表示调用类型的枚举值(如InvokeMetaMethod)、一个表示被调用方法的索引以及一个参数列表。
在这个例子中,qt_static_metacall函数的实现是这样的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void MyWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
auto *_t = static_cast<MyWidget *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->mySignal(); break; // 如果_id为0,表示调用mySignal信号
case 1: _t->mySlot(); break; // 如果_id为1,表示调用mySlot槽
default: ;
}
} else if (_c == QMetaObject::IndexOfMethod) {
// ...
}
Q_UNUSED(_a);
}