Qt 中 Q_OBJECT 宏及 moc_前缀文件

  • Q_OBJECT 宏
    • 元对象系统 (Meta-Object System)
    • 了解 Q_OBJECT 宏
    • 展开 Q_OBJECT 宏
  • MOC 预编译器
    • MOC (Meta-Object Compiler)
    • 前缀 moc_xx.cpp文件
      • 代码示例如下
      • MakeFile文件
      • 生成的moc文件如下
      • ==moc_whatismoc.cpp== 文件分析
        • 片段一
          • 结构体`qt_meta_stringdata_WhatIsMoc_t`
        • 片段二
          • 宏`QT_MOC_LITERAL`和`qt_meta_stringdata_WhatIsMoc`
        • 片段三
          • 数组`qt_meta_data_WhatIsMoc[]`
        • 片段四
          • 结构体`WhatIsMoc::staticMetaObject`
        • 片段五
          • 公有接口`metaObject`、`qt_metacall`、`qt_metacast`
          • 信号槽调用实现`qt_static_metacall`
          • 信号定义
  • 总结
  • 注意
  • 文章参考

Q_OBJECT 宏

元对象系统 (Meta-Object System)

  • Qt框架在C++基础上增加了元对象系统 (Meta-Object-System)
  • Qt中所有类全部继承自QObject类,这是使用元对象系统的前提
  • Qt中信号-槽机制是元对象系统提供的最重要内容,本质上是基于C++中函数、函数指针、回调函数等一系列语法构成,其实现与Q_OBJECT宏有关

了解 Q_OBJECT 宏

  • 启用 Meta-Object-System 一系列功能,如信号-槽机制
  • Q_OBJECT宏会对Qt中的类进行标识,以便被Qt提供的moc预编译器识别
  • moc预编译器将头文件中的Q_OBJECT 宏展开,若类的声明及定义在.cpp文件内,则预编译器不会产生相对应的moc_xx.cpp文件
  • Q_OBJECT宏不应当随意去除,不仅信号-槽机制依赖于该宏,其他一些例如国际化机制、不基于 C++ RTTI 的反射等能力也依赖于该宏

展开 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, "")
  • 由此不难看出,Qt源码中Q_OBJECT宏展开后,主要内容为 “元对象系统” 的一部分函数 (如信号-槽机制) 声明,但其实现并非在qobject.cpp文件中,而是在moc_qobject.cpp文件中
  • static const QMetaObject staticMetaObject;
    保存该类的元对象系统信息,为类的所有对象共享
  • virtual const QMetaObject *metaObject() const;
    这个函数通常情况下会返回类的静态元对象staticMetaObject的地址,如果在 QML 程序中,会返回非静态元对象
  • virtual int qt_metacall(QMetaObject::Call, int, void **);
    这个函数负责槽函数的调用和属性系统的读写,内部会调用下方函数
  • qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
    这个函数是槽函数调用机制的实现
  • QT_TR_FUNCTIONS
    这个宏负责实现翻译国际化字符串,展开如下
#  define QT_TR_FUNCTIONS \static inline QString tr(const char *s, const char *c = nullptr, int n = -1) \{ return staticMetaObject.tr(s, c, n); } \QT_DEPRECATED static inline QString trUtf8(const char *s, const char *c = nullptr, int n = -1) \{ return staticMetaObject.tr(s, c, n); }

MOC 预编译器

MOC (Meta-Object Compiler)

  • moc 即元对象系统的预编译器
  • Qt 将源代码交给标准 C++ 编译器 (如 gcc) 之前,需要事先将这些扩展的语法去除掉。完成这一操作的就是 moc。预处理器执行之后,Q_OBJECT宏将不存在
  • moc 读取一个C++头文件,如果它找到包含Q_OBJECT宏的一个或多个类声明,则会生成一个包含这些类的元对象代码的C++源文件,并且以moc_作为前缀
  • 信号和槽机制、运行时类型信息和动态属性系统都需要元对象代码。所以由moc生成的C++源文件必须编译并与类的实现联系起来。通常,moc无需手工调用,而是由构建系统自动调用的,因此它不需要程序员额外的工作。

前缀 moc_xx.cpp文件

  • moc 预编译后产生的对应类的元对象代码C++源文件

代码示例如下

  • whatismoc.h
#ifndef WHATISMOC_H
#define WHATISMOC_H#include <QObject>
#include <QString>
#include <iostream>class WhatIsMoc : public QObject
{Q_OBJECT
public:explicit WhatIsMoc(QObject *parent = nullptr);signals:void sigTest1(const QString& text);void sigTest2(const QString& text, int num);void sigTest3(const QString& text); // 不连接任何槽;public slots:void sltTest1(const QString& text);void sltTest2(const QString& text, int num);void sltTest3(const QString& text); // 不连接任何信号;
};#endif // WHATISMOC_H

自定义WhatIsMoc类继承自QObject,并分别定义信号与槽:
信号:sigTest1、sigTest2、sigTest3
槽:sltTest1、sltTest2、sltTest3
其中:sigTest3不与任何槽连接、sltTest3不与任何信号连接

  • whatismoc.cpp
#include "whatismoc.h"WhatIsMoc::WhatIsMoc(QObject *parent) : QObject(parent)
{connect(this, &WhatIsMoc::sigTest1, this, &WhatIsMoc::sltTest1);connect(this, &WhatIsMoc::sigTest2, this, &WhatIsMoc::sltTest2);
}void WhatIsMoc::sltTest1(const QString &text)
{std::cout << "sltTest1";
}void WhatIsMoc::sltTest2(const QString &text, int num)
{std::cout << "sltTest2";
}void WhatIsMoc::sltTest3(const QString &text)
{std::cout << "sltTest3";
}
  • main.cpp
#include <QCoreApplication>
#include "whatismoc.h"int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);WhatIsMoc moc;return a.exec();
}

MakeFile文件

  • 打开Qt自动生成的MakeFile文件,看到如下一行:
SOURCES       = ..\WhatIsMoc\main.cpp \..\WhatIsMoc\whatismoc.cpp debug\moc_whatismoc.cpp
  • 由此可知,Qt自动生成了moc_whatismoc.cpp文件

生成的moc文件如下

  • moc_whatismoc.cpp
/****************************************************************************
** Meta object code from reading C++ file 'whatismoc.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.9.9)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/#include "../../WhatIsMoc/whatismoc.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'whatismoc.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.9.9. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endifQT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_WhatIsMoc_t {QByteArrayData data[10];char stringdata0[74];
};
#define QT_MOC_LITERAL(idx, ofs, len) \Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \qptrdiff(offsetof(qt_meta_stringdata_WhatIsMoc_t, stringdata0) + ofs \- idx * sizeof(QByteArrayData)) \)
static const qt_meta_stringdata_WhatIsMoc_t qt_meta_stringdata_WhatIsMoc = {{QT_MOC_LITERAL(0, 0, 9), // "WhatIsMoc"
QT_MOC_LITERAL(1, 10, 8), // "sigTest1"
QT_MOC_LITERAL(2, 19, 0), // ""
QT_MOC_LITERAL(3, 20, 4), // "text"
QT_MOC_LITERAL(4, 25, 8), // "sigTest2"
QT_MOC_LITERAL(5, 34, 3), // "num"
QT_MOC_LITERAL(6, 38, 8), // "sigTest3"
QT_MOC_LITERAL(7, 47, 8), // "sltTest1"
QT_MOC_LITERAL(8, 56, 8), // "sltTest2"
QT_MOC_LITERAL(9, 65, 8) // "sltTest3"},"WhatIsMoc\0sigTest1\0\0text\0sigTest2\0num\0""sigTest3\0sltTest1\0sltTest2\0sltTest3"
};
#undef QT_MOC_LITERALstatic const uint qt_meta_data_WhatIsMoc[] = {// content:7,       // revision0,       // classname0,    0, // classinfo6,   14, // methods0,    0, // properties0,    0, // enums/sets0,    0, // constructors0,       // flags3,       // signalCount// signals: name, argc, parameters, tag, flags1,    1,   44,    2, 0x06 /* Public */,4,    2,   47,    2, 0x06 /* Public */,6,    1,   52,    2, 0x06 /* Public */,// slots: name, argc, parameters, tag, flags7,    1,   55,    2, 0x0a /* Public */,8,    2,   58,    2, 0x0a /* Public */,9,    1,   63,    2, 0x0a /* Public */,// signals: parametersQMetaType::Void, QMetaType::QString,    3,QMetaType::Void, QMetaType::QString, QMetaType::Int,    3,    5,QMetaType::Void, QMetaType::QString,    3,// slots: parametersQMetaType::Void, QMetaType::QString,    3,QMetaType::Void, QMetaType::QString, QMetaType::Int,    3,    5,QMetaType::Void, QMetaType::QString,    3,0        // eod
};void WhatIsMoc::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{if (_c == QMetaObject::InvokeMetaMethod) {WhatIsMoc *_t = static_cast<WhatIsMoc *>(_o);Q_UNUSED(_t)switch (_id) {case 0: _t->sigTest1((*reinterpret_cast< const QString(*)>(_a[1]))); break;case 1: _t->sigTest2((*reinterpret_cast< const QString(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break;case 2: _t->sigTest3((*reinterpret_cast< const QString(*)>(_a[1]))); break;case 3: _t->sltTest1((*reinterpret_cast< const QString(*)>(_a[1]))); break;case 4: _t->sltTest2((*reinterpret_cast< const QString(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break;case 5: _t->sltTest3((*reinterpret_cast< const QString(*)>(_a[1]))); break;default: ;}} else if (_c == QMetaObject::IndexOfMethod) {int *result = reinterpret_cast<int *>(_a[0]);{typedef void (WhatIsMoc::*_t)(const QString & );if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&WhatIsMoc::sigTest1)) {*result = 0;return;}}{typedef void (WhatIsMoc::*_t)(const QString & , int );if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&WhatIsMoc::sigTest2)) {*result = 1;return;}}{typedef void (WhatIsMoc::*_t)(const QString & );if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&WhatIsMoc::sigTest3)) {*result = 2;return;}}}
}const QMetaObject WhatIsMoc::staticMetaObject = {{ &QObject::staticMetaObject, qt_meta_stringdata_WhatIsMoc.data,qt_meta_data_WhatIsMoc,  qt_static_metacall, nullptr, nullptr}
};const QMetaObject *WhatIsMoc::metaObject() const
{return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}void *WhatIsMoc::qt_metacast(const char *_clname)
{if (!_clname) return nullptr;if (!strcmp(_clname, qt_meta_stringdata_WhatIsMoc.stringdata0))return static_cast<void*>(this);return QObject::qt_metacast(_clname);
}int WhatIsMoc::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{_id = QObject::qt_metacall(_c, _id, _a);if (_id < 0)return _id;if (_c == QMetaObject::InvokeMetaMethod) {if (_id < 6)qt_static_metacall(this, _c, _id, _a);_id -= 6;} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {if (_id < 6)*reinterpret_cast<int*>(_a[0]) = -1;_id -= 6;}return _id;
}// SIGNAL 0
void WhatIsMoc::sigTest1(const QString & _t1)
{void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };QMetaObject::activate(this, &staticMetaObject, 0, _a);
}// SIGNAL 1
void WhatIsMoc::sigTest2(const QString & _t1, int _t2)
{void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)) };QMetaObject::activate(this, &staticMetaObject, 1, _a);
}// SIGNAL 2
void WhatIsMoc::sigTest3(const QString & _t1)
{void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };QMetaObject::activate(this, &staticMetaObject, 2, _a);
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE

moc_whatismoc.cpp 文件分析

片段一

结构体qt_meta_stringdata_WhatIsMoc_t
  • moc 文件的开头为结构体
struct qt_meta_stringdata_WhatIsMoc_t {QByteArrayData data[10]; //信号函数3个、信号函数各个参数共4个、槽函数3个char stringdata0[74];
};

结构体内含两个数组 其中QByteArrayDataqarraydata.h

  • QByteArrayData中的一部分定义
struct Q_CORE_EXPORT QArrayData
{QtPrivate::RefCount ref;int size;uint alloc : 31;uint capacityReserved : 1;qptrdiff offset; // in bytes from beginning of headervoid *data(){Q_ASSERT(size == 0|| offset < 0 || size_t(offset) >= sizeof(QArrayData));return reinterpret_cast<char *>(this) + offset;}const void *data() const{Q_ASSERT(size == 0|| offset < 0 || size_t(offset) >= sizeof(QArrayData));return reinterpret_cast<const char *>(this) + offset;}// 以下省略
};

size是指针指向数据的大小 offset是数据指针和当前对象指针的距离(偏移量)
如果数据大小不是0,data函数就根据偏移量offset和当前对象的地址返回当前数据的指针

片段二

QT_MOC_LITERALqt_meta_stringdata_WhatIsMoc
  • QT_MOC_LITERAL的实现
#define QT_MOC_LITERAL(idx, ofs, len) \Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \qptrdiff(offsetof(qt_meta_stringdata_WhatIsMoc_t, stringdata0) + ofs \- idx * sizeof(QByteArrayData)) \)

分为两部分
宏:Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET
参数:(len, qptrdiff(offsetof(qt_meta_stringdata_moctest_t, stringdata0) + ofs - idx * sizeof(QByteArrayData)))

部分一
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET展开如下

#define Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset)#define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \{ Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, offset } \

{}里面的内容正好用来初始化QArrayData中的成员变量
size对应Q_REFCOUNT_INITIALIZE_STATIC,值是-1
alloccapacityReserved值都是0
offset对应offset

部分二
参数(len, qptrdiff(offsetof(qt_meta_stringdata_moctest_t, stringdata0) + ofs - idx * sizeof(QByteArrayData)))

len对应size
qptrdiff就是一个数,数据类型为int, offsetof用于求结构体中一个成员在该结构体中的偏移量,在stddef.h头文件中, qptrdiff(offsetof(qt_meta_stringdata_moctest_t, stringdata0) + ofs - idx * sizeof(QByteArrayData))对应offset
算出来这两个数据的值,然后初始化QByteArrayData数组

  • 结构体qt_meta_stringdata_WhatIsMoc内容

static const qt_meta_stringdata_WhatIsMoc_t qt_meta_stringdata_WhatIsMoc = {{QT_MOC_LITERAL(0, 0, 9), // "WhatIsMoc"
QT_MOC_LITERAL(1, 10, 8), // "sigTest1"
QT_MOC_LITERAL(2, 19, 0), // ""
QT_MOC_LITERAL(3, 20, 4), // "text"
QT_MOC_LITERAL(4, 25, 8), // "sigTest2"
QT_MOC_LITERAL(5, 34, 3), // "num"
QT_MOC_LITERAL(6, 38, 8), // "sigTest3"
QT_MOC_LITERAL(7, 47, 8), // "sltTest1"
QT_MOC_LITERAL(8, 56, 8), // "sltTest2"
QT_MOC_LITERAL(9, 65, 8) // "sltTest3"},"WhatIsMoc\0sigTest1\0\0text\0sigTest2\0num\0""sigTest3\0sltTest1\0sltTest2\0sltTest3"
};
#undef QT_MOC_LITERAL

这个结构体就是片段一中的结构体 该结构体的第一个数组就是QByteArrayData
所以宏QT_MOC_LITERAL返回的一定是QByteArrayData类型数据
QT_MOC_LITERAL宏后面的字符串对应片段一中结构体qt_meta_stringdata_WhatIsMoc_t中的第二个数组
字符串的内容分别是类名、信号、信号参数、槽

  • 总结

这一部分会通过宏QT_MOC_LITERAL初始化片段一中结构体qt_meta_stringdata_WhatIsMoc_t

片段三

数组qt_meta_data_WhatIsMoc[]
  • 数组qt_meta_data_WhatIsMoc[]内容
static const uint qt_meta_data_WhatIsMoc[] = {// content:7,       // revision0,       // classname0,    0, // classinfo6,   14, // methods0,    0, // properties0,    0, // enums/sets0,    0, // constructors0,       // flags3,       // signalCount// signals: name, argc, parameters, tag, flags1,    1,   44,    2, 0x06 /* Public */,4,    2,   47,    2, 0x06 /* Public */,6,    1,   52,    2, 0x06 /* Public */,// slots: name, argc, parameters, tag, flags7,    1,   55,    2, 0x0a /* Public */,8,    2,   58,    2, 0x0a /* Public */,9,    1,   63,    2, 0x0a /* Public */,// signals: parametersQMetaType::Void, QMetaType::QString,    3,QMetaType::Void, QMetaType::QString, QMetaType::Int,    3,    5,QMetaType::Void, QMetaType::QString,    3,// slots: parametersQMetaType::Void, QMetaType::QString,    3,QMetaType::Void, QMetaType::QString, QMetaType::Int,    3,    5,QMetaType::Void, QMetaType::QString,    3,0        // eod
};
  • 数组分解

部分一

 // content:7,       // revision0,       // classname0,    0, // classinfo6,   14, // methods0,    0, // properties0,    0, // enums/sets0,    0, // constructors0,       // flags3,       // signalCount

revison:版本号,不用管
classname:类名在stringdata0中的索引,总是0,qt_meta_data_WhatIsMoc.data[0].data()
classinfo:不用管
methods:信号和槽总数量6,从qt_meta_data_WhatIsMoc.data[13]开始描述
properties:属性数量0,数组起始位置0
signalCount:信号数量3

这些数据存储在如下结构体QMetaObjectPrivate

struct QMetaObjectPrivate
{// revision 7 is Qt 5.0 everything lower is not supported// revision 8 is Qt 5.12: It adds the enum name to QMetaEnumenum { OutputRevision = 8 }; // Used by moc, qmetaobjectbuilder and qdbusint revision;int className;int classInfoCount, classInfoData;int methodCount, methodData;int propertyCount, propertyData;int enumeratorCount, enumeratorData;int constructorCount, constructorData;int flags;int signalCount;//以下省略
};

部分二

 // signals: name, argc, parameters, tag, flags1,    1,   44,    2, 0x06 /* Public */,4,    2,   47,    2, 0x06 /* Public */,6,    1,   52,    2, 0x06 /* Public */,// slots: name, argc, parameters, tag, flags7,    1,   55,    2, 0x0a /* Public */,8,    2,   58,    2, 0x0a /* Public */,9,    1,   63,    2, 0x0a /* Public */,

第一列:表示信号槽的索引
第二列:表示信号槽函数的参数个数
第三列:表示参数数据的描述是从数组qt_meta_data_WhatIsMoc.data[n]开始描述的
第四列:表示tag信息
第五列:表示信号槽函数标志位

信号槽函数标志位在qmetaobject_p.h里声明,声明如下

enum MethodFlags  {AccessPrivate = 0x00,AccessProtected = 0x01,AccessPublic = 0x02,AccessMask = 0x03, //maskMethodMethod = 0x00,MethodSignal = 0x04,MethodSlot = 0x08,MethodConstructor = 0x0c,MethodTypeMask = 0x0c,MethodCompatibility = 0x10,MethodCloned = 0x20,MethodScriptable = 0x40,MethodRevisioned = 0x80
};

将上述枚举按位或,就能得到第五列数据
以第一行数据1, 1, 44, 2, 0x06 /* Public */为例,0x06 = 0x02 | 0x04,表示public signal

部分三

 // signals: parametersQMetaType::Void, QMetaType::QString,    3,QMetaType::Void, QMetaType::QString, QMetaType::Int,    3,    5,QMetaType::Void, QMetaType::QString,    3,// slots: parametersQMetaType::Void, QMetaType::QString,    3,QMetaType::Void, QMetaType::QString, QMetaType::Int,    3,    5,QMetaType::Void, QMetaType::QString,    3,0        // eod

以第一行数据QMetaType::Void, QMetaType::QString, 3,为例
QMetaType::Void:返回参数
QMetaType::QString:形参

片段四

结构体WhatIsMoc::staticMetaObject
  • 结构体内容
const QMetaObject WhatIsMoc::staticMetaObject = {{ &QObject::staticMetaObject, qt_meta_stringdata_WhatIsMoc.data,qt_meta_data_WhatIsMoc,qt_static_metacall, nullptr, nullptr}
};

WhatIsMoc::staticMetaObject的类型是QMetaObject

QMetaObject的结构在qobjectdefs.h中声明

struct Q_CORE_EXPORT QMetaObject
{struct { // private dataSuperData superdata;const QByteArrayData *stringdata;const uint *data;typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);StaticMetacallFunction static_metacall;const SuperData *relatedMetaObjects;void *extradata; //reserved for future use} d;
};

superdata:初始值为QMetaObject::SuperData::link<QObject::staticMetaObject>(),表示基类的元对象数据
stringdata:初始值为qt_meta_stringdata_WhatIsMoc.data,指向的是qt_meta_stringdata_WhatIsMoc_t中的data数组
data:初始值为qt_meta_data_WhatIsMoc,该指针指向数组qt_meta_data_WhatIsMoc
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);声明了一个函数指针,指针的类型是void (*)(QObject *, QMetaObject::Call, int, void **);
static_metacall:初始值为qt_static_metacall
relatedMetaObjects:相关元对象指针
extradata:额外数据

片段五

公有接口metaObjectqt_metacallqt_metacast
  • 公有接口的定义
//获取元对象,可以调用this->metaObject()->className();获取类名称
const QMetaObject *WhatIsMoc::metaObject() const
{return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}//这个函数负责将传递来到的类字符串描述转化为void*
void *WhatIsMoc::qt_metacast(const char *_clname)
{if (!_clname) return nullptr;if (!strcmp(_clname, qt_meta_stringdata_WhatIsMoc.stringdata0))return static_cast<void*>(this);return QObject::qt_metacast(_clname);
}//调用方法
int WhatIsMoc::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{_id = QObject::qt_metacall(_c, _id, _a);if (_id < 0)return _id;if (_c == QMetaObject::InvokeMetaMethod) {if (_id < 6)qt_static_metacall(this, _c, _id, _a);_id -= 6;} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {if (_id < 6)*reinterpret_cast<int*>(_a[0]) = -1;_id -= 6;}return _id;
}

这些接口是可以在自己代码里直接调用

信号槽调用实现qt_static_metacall
  • 接口定义
void WhatIsMoc::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{if (_c == QMetaObject::InvokeMetaMethod) {WhatIsMoc *_t = static_cast<WhatIsMoc *>(_o);Q_UNUSED(_t)switch (_id) {case 0: _t->sigTest1((*reinterpret_cast< const QString(*)>(_a[1]))); break;case 1: _t->sigTest2((*reinterpret_cast< const QString(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break;case 2: _t->sigTest3((*reinterpret_cast< const QString(*)>(_a[1]))); break;case 3: _t->sltTest1((*reinterpret_cast< const QString(*)>(_a[1]))); break;case 4: _t->sltTest2((*reinterpret_cast< const QString(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break;case 5: _t->sltTest3((*reinterpret_cast< const QString(*)>(_a[1]))); break;default: ;}} else if (_c == QMetaObject::IndexOfMethod) {int *result = reinterpret_cast<int *>(_a[0]);{typedef void (WhatIsMoc::*_t)(const QString & );if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&WhatIsMoc::sigTest1)) {*result = 0;return;}}{typedef void (WhatIsMoc::*_t)(const QString & , int );if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&WhatIsMoc::sigTest2)) {*result = 1;return;}}{typedef void (WhatIsMoc::*_t)(const QString & );if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&WhatIsMoc::sigTest3)) {*result = 2;return;}}}
}

执行对象所对应的信号或槽
对信号槽函数进行参数检查

信号定义
// SIGNAL 0
void WhatIsMoc::sigTest1(const QString & _t1)
{void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };QMetaObject::activate(this, &staticMetaObject, 0, _a);
}// SIGNAL 1
void WhatIsMoc::sigTest2(const QString & _t1, int _t2)
{void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)) };QMetaObject::activate(this, &staticMetaObject, 1, _a);
}// SIGNAL 2
void WhatIsMoc::sigTest3(const QString & _t1)
{void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };QMetaObject::activate(this, &staticMetaObject, 2, _a);
}

信号之所以不用定义,是因为其定义有Qt框架完成,并与类的源文件一同编译
信号函数实现:

  • 定义一个void*的指针数组,数组的第一个元素都是空,给元对系统内部注册元方法参数类型时使用,剩下的元素都是信号函数的参数的指针,然后将数组传入activate

总结

  • Q_OBJECT是Qt框架建立元对象系统的关键部分
  • moc文件是关于宏Q_OBJECT中函数声明的定义以及结构体的赋值
  • 信号-槽机制就是建立“注册索引”的机制

注意

  • 文章内容以信号-槽connect函数第五个参数是默认为前提

文章参考

深入分析Qt信号与槽(下)
MOC文件解读(上)——MOC文件中的数据
MOC文件解读(下)——MOC文件中的函数

Qt 中 Q_OBJECT 宏及 moc_*.cpp文件相关推荐

  1. Qt中Q_OBJECT与生成的moc文件的作用

    一.先来了解Q_OBJECT 只有继承了QObject类的类,才具有信号槽的能力.所以,为了使用信号槽,必须继承QObject.凡是QObject类(不管是直接子类还是间接子类),都应该在第一行代码写 ...

  2. qmoc文件_Qt中Q_OBJECT与生成的moc文件的作用

    Qt中Q_OBJECT与生成的moc文件的作用 一.先来了解Q_OBJECT 只有继承了QObject类的类,才具有信号槽的能力.所以,为了使用信号槽,必须继承QObject.凡是QObject类(不 ...

  3. qt中生成含有中文的json文件,读取含有中文的json文件

    引言 之前将变量保存并在本地生成json文件,由于其中含有中文,导致生成的json文件出现乱码,或者就是生成的json文件没有乱码,但是读取生成的json文件时出现乱码,不能正常解析json. 示例 ...

  4. 解决qt中cmake单独存放 .ui, .cpp, .h文件

    创建时间:2023-02-18 17:58:04 设想 项目文件较多,全部放在一个目录下就像依托答辩. 希望能将头文件放入include,ui文件放入ui,源文件放入src. 为了将Qt代码和一般非Q ...

  5. 2.关于QT中的Dialog(模态窗口),文件选择器,颜色选择器,字体选择器,消息提示窗口

     1 新建一个空项目 A 编写 .pro文件 QT += gui widgets HEADERS += \ MyDialog.h SOURCES += \ MyDialog.cpp B 编写MyD ...

  6. Qt中Q_D宏及d指针

    原文标题:d指针在Qt上的应用及实现 原文链接:http://blog.csdn.net/rabinsong/article/details/9474859 正文: Qt为了使其动态库最大程度上实现二 ...

  7. BlackBerry 10:Qt中使用OpenAL播放wav声音文件

    BlackBerry 10/PlayBook OS 2.0里面都内置OpenAL的类库,所以在程序中使用OpenAL类库播放声音是很方便的. BlackBerry 10开发者网站上提供的例子程序Cow ...

  8. qt中生成含有中文的json文件和解析json文件

    提要 生成的json文件在程序加载解析时出现上述错误,究其原因是生成json文件过程中编码问题.qt编译器默认的编码格式为utf-8,而windows一般为gbk编码,所以就需要在生成本地json文件 ...

  9. Qt笔记-Qt中Json存二进制文件并读取文件(QJsonObject、QJsonArray、QJsonDocument的使用)

    程序运行截图如下: 文件如下: 源码如下: #include <QCoreApplication> #include <QJsonDocument> #include < ...

  10. QT中pro、pri、prf、prl文件

    QT .pro文件 在QT中使用qmake自动生成pro文件,如果要自己定制工程选项,则需要自行修改pro文件. pro文件有以下关键字:TEMPLATE.TARGET.DESTDIR.DEPENDP ...

最新文章

  1. 关于ARP、MAC、IP欺骗以及TCP劫持
  2. java如何使显示字符下标_Java如何在 Word 中设置上、下标
  3. linux 的git的安装目录,Linux下Git安装及配置较详细-Go语言中文社区
  4. oracle 查找上一个,查看ORACLE_HOME的另外一个方法
  5. Linux安装redis和部署
  6. 贺利坚老师汇编课程37笔记:运用栈加两层循环之把六个字符串里的字母都改写成大写字母
  7. Eclipse启动报错:A java runtime Environment(JRE) or java Development……的解决办法
  8. 算法 第四版 2.1.25 不需要交换的插入排序
  9. 如何实现某个软件的单独断网/稳定地解决fluent断网计算中止问题/tecplot联网后打开程序出现报错问题
  10. 软件构造笔记---Java的多态性
  11. WIN7系统的虚拟机C盘扩容步骤
  12. 深度至尊 GHOST XP SP3 纯净版 V4.0
  13. java.lang.Byte常用方法
  14. GSR引擎专用工具更新使用贴子
  15. CCF201509-1数列分段(C语言)
  16. 挂载zookeeper到文件系统 (mount zookeeper)
  17. css 从右到左滚动,CSS 文字从左到右滚动 (右进左出)
  18. MTK 按键驱动流程总结
  19. 2016年8月23日 星期二 --出埃及记 Exodus 16:24
  20. 修复恢复可疑的SQLServer数据库

热门文章

  1. 使用Windows Live ID登录网站
  2. linux环境下,实现公共聊天室功能
  3. 7款强大的免费PDF批量分割软件【附下载】
  4. 分享三种在Excel表格中自动求和的方法
  5. 如何计算前缀,网络,子网和主机号?
  6. 北大先修课 计算机,北大先修课
  7. 如何将pdf转换成word的3种免费方法
  8. 微软Google人才战主角首次开口:Google让我震撼(转)
  9. python数据拟合之scipy.optimize.curve_fit
  10. 软件测试工程师...我是如何正确地申请加薪的!