QT(1):MOC元对象编译器和moc文件

MOC——元对象编译器(Meta Object Compiler)

它是Qt提供的一个工具,在构建过程中用于为使用Qt元对象系统的类生成额外的C++代码(moc_class.cpp)。元对象系统使得Qt应用程序能够使用信号和槽、内省和动态属性处理等特性。

Moc在交给gcc进行标准编译之前先调用moc分析源文件,如果发现Q_OBJECT在源文件前加上moc_生成moc_a.cpp将这个新文件加入编译系统,最终被连接到二进制代码中。

/* qmake ignore Q_OBJECT */
#define Q_OBJECT \
public: \
    QT_WARNING_PUSH \        //在编译过程中临时禁用警告信息
    Q_OBJECT_NO_OVERRIDE_WARNING \      //禁用对未重写(override)虚函数的警告
    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 \        //自动生成翻译字符串的相关函数tr()、trUtf8()、trId()等
private: \
    Q_OBJECT_NO_ATTRIBUTES_WARNING \        //禁用对属性(attributes)的警告。
    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, "")

定义一个类test:

#ifndef TEST_H
#define TEST_H

#include 

class Test : public QObject
{
    Q_OBJECT
public:
    explicit Test(QObject *parent = nullptr);
    
public slots:
    void slotTest(int test);

signals:
    void signalTest(int test);
};

#endif // TEST_H

由上面的头文件生成的moc_class.cpp文件中包含:

1.结构体qt_meta_stringdata_Test

struct qt_meta_stringdata_Test_t {
    QByteArrayData data[5];
    char stringdata0[31];
};

存储类名、属性名、函数名、信号名…
包括两个变量,各个名称的数组和所有名称的拼接

2.数组qt_meta_data_Test[]

static const uint qt_meta_data_Test[] = {
 // 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,    1,   24,    2, 0x06 /* Public */,
 // slots: name, argc, parameters, tag, flags
       4,    1,   27,    2, 0x0a /* Public */,
 // signals: parameters
    QMetaType::Void, QMetaType::Int,    3,
 // slots: parameters
    QMetaType::Void, QMetaType::Int,    3,
       0        // eod
};

元对象索引信息表,存储了Stringdata数据的数量和索引、信息

3.元对象类Test::staticMetaObject——静态元对象

QT_INIT_METAOBJECT const QMetaObject Test::staticMetaObject = { {
    QMetaObject::SuperData::link<QObject::staticMetaObject>(),
    qt_meta_stringdata_Test.data,
    qt_meta_data_Test,
    qt_static_metacall,
    nullptr,
    nullptr
} };

1.对元对象信息进行操作,偏移量、数量、索引、通过索引获取对象
2.接口,调用成员函数、信号激活、方法检测(check_…)
3.存储元对象信息

4.函数Test::metaObject()返回静态元对象

const QMetaObject *Test::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

5.函数Test::qt_metacast()转化返回为QObject对象

void *Test::qt_metacast(const char *_clname)
{
    if (!_clname) return nullptr;
    if (!strcmp(_clname, qt_meta_stringdata_Test.stringdata0))
        return static_cast<void*>(this);
    return QObject::qt_metacast(_clname);
}

6.函数Test::qt_static_metacall()槽函数和属性读写

void Test::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        auto *_t = static_cast<Test *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->signalTest((*reinterpret_cast< int(*)>(_a[1]))); break;//索引0调用信号
        case 1: _t->slotTest((*reinterpret_cast< int(*)>(_a[1]))); break;//索引1调用槽函数
        default: ;
        }
    //单独对信号的调用
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        {
            using _t = void (Test::*)(int );// _t 定义为指向 Test 类的成员函数指针
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Test::signalTest)) {
                *result = 0;
                return;
            }
        }
    }
}

函数会根据调用类型和索引来判断执行的操作类型,比如调用方法、设置属性值等。通常用于 Qt 的信号与槽机制的底层实现。
1.QObject _o:元对象调用的实例
2.QMetaObject::Call _c:调用类型,表示要执行的操作类型。可以是 QMetaObject::InvokeMetaMethod (调用方法)、 QMetaObject::IndexOfMethod()、 QMetaObject::ReadProperty (读取属性值)、 QMetaObject::WriteProperty (设置属性值)等
3.int _id :索引,表示要调用的方法或属性的索引。索引是在元对象中分配的唯一标识符,用于标识方法和属性。
4. void argv
* :指向参数列表的指针。参数列表是一个 void* 数组,用于存储方法调用时的参数值。每个参数值都是一个 void* 类型的指针,需要根据方法的参数类型进行适当的转换。

7.函数Test::qt_metacall()调用了qt_static_metacall()

int Test::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 < 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;
}

8.函数Test::signalTest()信号定义

// SIGNAL 0
void Test::signalTest(int _t1)
{
    //第一个元素是返回值void,之后依次是参数
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);//用于在信号发射函数中触发槽函数
}

当在代码中发出一个信号时,实际上是通过调用这个信号函数来触发信号的发射。信号函数会根据信号的参数和连接的槽函数的参数进行匹配,并生成相应的代码来调用槽函数。

你可能感兴趣的:(QT,qt,c++)