Qt 中 Q_OBJECT 宏及 moc_*.cpp文件

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.)"
#endif

QT_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_LITERAL

static const uint qt_meta_data_WhatIsMoc[] = {

 // content:
       7,       // revision
       0,       // classname
       0,    0, // classinfo
       6,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       3,       // signalCount

 // signals: name, argc, parameters, tag, flags
       1,    1,   44,    2, 0x06 /* Public */,
       4,    2,   47,    2, 0x06 /* Public */,
       6,    1,   52,    2, 0x06 /* Public */,

 // slots: name, argc, parameters, tag, flags
       7,    1,   55,    2, 0x0a /* Public */,
       8,    2,   58,    2, 0x0a /* Public */,
       9,    1,   63,    2, 0x0a /* Public */,

 // signals: parameters
    QMetaType::Void, QMetaType::QString,    3,
    QMetaType::Void, QMetaType::QString, QMetaType::Int,    3,    5,
    QMetaType::Void, QMetaType::QString,    3,

 // slots: parameters
    QMetaType::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 header
 
    void *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,       // revision
       0,       // classname
       0,    0, // classinfo
       6,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       3,       // signalCount

 // signals: name, argc, parameters, tag, flags
       1,    1,   44,    2, 0x06 /* Public */,
       4,    2,   47,    2, 0x06 /* Public */,
       6,    1,   52,    2, 0x06 /* Public */,

 // slots: name, argc, parameters, tag, flags
       7,    1,   55,    2, 0x0a /* Public */,
       8,    2,   58,    2, 0x0a /* Public */,
       9,    1,   63,    2, 0x0a /* Public */,

 // signals: parameters
    QMetaType::Void, QMetaType::QString,    3,
    QMetaType::Void, QMetaType::QString, QMetaType::Int,    3,    5,
    QMetaType::Void, QMetaType::QString,    3,

 // slots: parameters
    QMetaType::Void, QMetaType::QString,    3,
    QMetaType::Void, QMetaType::QString, QMetaType::Int,    3,    5,
    QMetaType::Void, QMetaType::QString,    3,

       0        // eod
};
  • 数组分解

部分一

 // content:
       7,       // revision
       0,       // classname
       0,    0, // classinfo
       6,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       3,       // 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 QMetaEnum
    enum { OutputRevision = 8 }; // Used by moc, qmetaobjectbuilder and qdbus
 
    int 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, flags
       1,    1,   44,    2, 0x06 /* Public */,
       4,    2,   47,    2, 0x06 /* Public */,
       6,    1,   52,    2, 0x06 /* Public */,

 // slots: name, argc, parameters, tag, flags
       7,    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, //mask
 
    MethodMethod = 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: parameters
    QMetaType::Void, QMetaType::QString,    3,
    QMetaType::Void, QMetaType::QString, QMetaType::Int,    3,    5,
    QMetaType::Void, QMetaType::QString,    3,

 // slots: parameters
    QMetaType::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 data
        SuperData 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文件中的函数

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值