QT源码剖析-QT对象通信机制信号槽的绑定具体实现

本文详细介绍QT核心机制之一 信号和槽。

我们在此根据Qt源代码一步一步探究其信号槽的实现过程。

核心知识点:

模板元编程技术、 Qt moc预编译机制、 QObject类。

目录

1. QObject类介绍:

2: 相关助手类介绍:

2.1 类型(函数指针)萃取模板元函数。

2.2 函数调用器FunctorCall元函数

3. 信号槽需要Qt moc预编译器的支持

4. 信号槽绑定入口: QObject类的静态模板方法(connect方法)

4.1 从入口开始谈起,先看看QObject类的connect 静态模板方法对于信号和槽绑定的声明及其实现。

5. 信号发送(调用 emit)

6. 总结


1. QObject类介绍:

QObject类是整个Qt框架对象的核心模型,此类提供了大量Qt特性。包括对象的无缝通信机制信号槽、反射、动态属性、定时、对象树生命周期管理等。

2: 相关助手类介绍:

信号槽的本质是两个对象的函数地址映射,下面对信号槽实现过程中的一些助手类进行介绍。

2.1 类型(函数指针)萃取模板元函数。

注:下文中会把类型萃取模板元函数称之为类型萃取器。

FunctionPointer萃取器声明部分(Qt 信号槽绑定过程中会大量使用此萃取器来提取信号槽中的相关类型)

FunctionPointer偏特化版本之一:非CV的成员函数指针类型偏特化版本。

文中仅贴出此偏特化版本。为保证篇幅不至于太长,其他偏特化版本不做介绍基本大同小异。

从图中可以看出,FunctionPointer非CV类的成员函数指针类型偏特化版本主要做以下事情。

a. 提取类成员函数指针类型所属的类类型。b. 提取类成员函数指针类型函数形参参数包类型(形参列表打包到List模板元函数中)。

c. 提取类成员函数指针返回值类型。d. 重定义类成员函数指针类型。

e. 提取类成员函数指针类型的形参数量和是否为类的成员函数枚举值。

f. 提供一个此函数指针类型的分发调用静态方法。

2.2 函数调用器FunctorCall元函数

看看2.1 节萃取器会发现类型萃取包装的call分发函数会转而调用此元函数。(包装主要原因是解信号形参数量参数包)

此元函数的作用是调用指定对象o的成员函数f, 并解包信号函数形参数量参数包。

先记得这两个助手类,信号槽绑定与信号发射过程会大量使用到。

3. 信号槽需要Qt moc预编译器的支持

此处不对Qt moc预编译机制做详细介绍,只是谈谈它做了哪些事。 Qt moc详细实现原理单独博文在介绍。

首先我们看看moc预编译器对包含Q_OBJECT宏的类做了哪些事件。

举个栗子 定义一个TestObject类:

 

图中源码代码中Q_OBJECT宏必须添加,Qt moc预编译器根据此标识在编译前来生成moc_xx.cpp 实现文件。

编译后产生如下文件:

moc_testobject.cpp

void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        auto *_t = static_cast<TestObject *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->sig_void(); break;
        case 1: _t->sig_int((*reinterpret_cast< int(*)>(_a[1]))); break;
        case 2: _t->sig_char((*reinterpret_cast< char(*)>(_a[1]))); break;
        case 3: _t->sig_qstring((*reinterpret_cast< QString(*)>(_a[1]))); break;
        case 4: _t->sig_int_char((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< char(*)>(_a[2]))); break;
        case 5: _t->slot1(); break;
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        {
            using _t = void (TestObject::*)();
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&TestObject::sig_void)) {
                *result = 0;
                return;
            }
        }
        {
            using _t = void (TestObject::*)(int );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&TestObject::sig_int)) {
                *result = 1;
                return;
            }
        }
        {
            using _t = void (TestObject::*)(char );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&TestObject::sig_char)) {
                *result = 2;
                return;
            }
        }
        {
            using _t = void (TestObject::*)(QString );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&TestObject::sig_qstring)) {
                *result = 3;
                return;
            }
        }
        {
            using _t = void (TestObject::*)(int , char );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&TestObject::sig_int_char)) {
                *result = 4;
                return;
            }
        }
    }
}

QT_INIT_METAOBJECT const QMetaObject TestObject::staticMetaObject = { {
    QMetaObject::SuperData::link<QObject::staticMetaObject>(),
    qt_meta_stringdata_TestObject.data,
    qt_meta_data_TestObject,
    qt_static_metacall,
    nullptr,
    nullptr
} };


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

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

int TestObject::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 TestObject::sig_void()
{
    QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}

// SIGNAL 1
void TestObject::sig_int(int _t1)
{
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };
    QMetaObject::activate(this, &staticMetaObject, 1, _a);
}

// SIGNAL 2
void TestObject::sig_char(char _t1)
{
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };
    QMetaObject::activate(this, &staticMetaObject, 2, _a);
}

// SIGNAL 3
void TestObject::sig_qstring(QString _t1)
{
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };
    QMetaObject::activate(this, &staticMetaObject, 3, _a);
}

// SIGNAL 4
void TestObject::sig_int_char(int _t1, char _t2)
{
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))), const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t2))) };
    QMetaObject::activate(this, &staticMetaObject, 4, _a);
}

 

看到这个文件我想会打消这样一个疑问:为什么声明了信号函数自己没有实现编译器确没有报错?

这是因为Qt moc预编译器在编译器介入之前已经帮你做好了这件事。

moc_testobject.cpp 还定义了一些函数如qt_static_metacall、metaobject、qt_metacast、qt_metacall 为什么定义这些函数?

我们接着来看TestObject类声明中的Q_OBJECT宏内容:

: O_OBJECT 宏声明了与moc_testobject.cpp

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值