Qt: 信号与槽机制

一、信号和槽机制是什么?


        1 Qt信号槽机制:是Qt的核心机制,它是Qt定义的一种对象间的通讯机制,且独立于标准C/C++语言。

        2 信号(signals):当某个类对象发生内部改变时,发射“信号”随后与关联的“槽函数”被立即执行,“信号槽机制”完全独立于任何GUI事件循环机制

                (1)信号可以链接普通函数

                (2)如果存在一个信号对应多个槽函数,则槽函数调用顺序与connect顺序相同

     3 槽函数(slots):等同于类普通成员函数

     4 信号槽数量:单信号可以对应多个槽函数,单个槽函数也可以对应多个信号,互为一对多,多对一的关系

     5 信号槽连接(connect):注册信号与槽函数对其进行绑定,实现存储和调度

     6 应用场景

        (1)单线程:相当于函数指针调用

        (2)多线程:信号触发时,发送者线程将槽函数的调用转化为一次“调用事件”放入事件循环队列,接收者线程执行到下一次事件处理时,处理“调用事件”,调用相应的函数。

      7 使用注意:

                (1)使用方式灵活但是存在效率上的开销(损耗相对较少),不能像真正的“回调函数”高效

                (2)参数列表中不能使用宏定义和函数指针

                (3)参数列表中不能使用缺省值

                (4)参数列表中不能使用模版类

                (5)signals前不能设置访问权限(public private protected)且返回执行必须是void ,slots可以设置访问权限 返回值类型void

                 (6) signals和slots区域内不能含有:构造函数,内部类(嵌套类)、友元声明(类或函数)

                (7)signals 的参数类型和参数个数 == 对应的slots的参数类型和参数个数

        8 优缺点

                优点

                        1 类型安全,相关联的信号槽签名等同(signals 的参数类型和参数个数 == 对应的slots的参数类型和参数个数)

                        2 松耦合,削弱QT对象之间的耦合度(两个对象都互相不知道对方的存在,依然可以建立联系,关联运行时删除,程序不崩溃)

                缺点

                        1 效率:信号触发到槽函数调用比直接调用非虚函数速度慢10倍

        9 特征

                1 信号槽是观察者模式的一种实现

                2 一个信号就是一个能够被观察的事件,或者至少是事件已经发送的一种通知

                3 一个槽就是一个观察者,通常就是在被观察的对象发生变化的时候,被调用的函数

       10 信号槽本质上是两个对象的函数地址映射

 观察者设计模式 

        

耦合度:

        1 模块和模块之间信息或者参数的的依赖程度

        2 一般而言,低耦合性程序可读性可维护性比较好

详细介绍:设计模式:观察者模式(Observer)_~怎么回事啊~的博客-CSDN博客 

连接方式和类型

标准连接方式

1 语法

	connect(this, SIGNAL(signal_test1()) , this, SLOT(slot_test1()));

2 原理:将信号槽的函数名称按照相应规则组合成字符串,通过connect解析该字符串,分别获取信号槽的绝对索引,然后将信号槽的链接关系,添加到信号槽容器中

3 注意事项:所有检查都在运行时,通过解析字符串进行,如果字符串拼写错误却编译成功,错误则创建的是空链接,在运行时报错

4 特性

        1 不可以调用普通成员函数

        2 支持使用重载信号&&槽函数

5 链接类型

        1 默认 Qt::AutoConnection 直接链接

        2 设置:可手动设置

6 内部实现:QObject::connnect

函数指针连接方式(qt5及以上)

1语法

	connect(this,&qttest::signal_test1,this,&qttest::slot_test1);

2 原理:编译时检查,使用函数指针作为信号槽函数

3 注意事项编译时如果信号槽出现拼写错误则无法通过编译

4 特性

        1 调用逻辑层次分明,在编译时检查避免程序运行时错

        2 可以调用普通成员函数,槽函数

        3 不能定义重载信号和槽函数,否则无法识别

5 解决信号重载的方式,不能解决槽函数和类普通成员函数重载

        使用QOverload

6 链接类型

        1 默认 Qt::AutoConnection 直接链接

        2 设置:可手动设置

7 内部实现

        template<typename Func1,typename Func2>

        static inline QMeataObjectConnection connnet

lambda表达式连接方式

1 语法:

	connect(this, &qttest::signal_test1, [this]() {slot_test1(); });

2 原理:编译时检查,使用函数指针作为信号槽函数,编译时如果信号槽出现拼写错误则无法通过编译,lambda表达式发挥的是槽函数的函数指针

3 注意事项:同函数指针

4 特性

        1 支持槽函数重载

        2 不支持信号重载

5 解决

          使用QOverload

6 链接类型

        1 默认 Qt::AutoConnection 直接链接

        2 设置:可手动设置

连接类型

enum ConnectionType {
	AutoConnection,//默认
	DirectConnection,//立即调用
	QueuedConnection,//放在接收者队列
	BlockingQueuedConnection,//与放在接收者队列,同时阻塞发送者
	UniqueConnection =  0x80//标志位,避免重复链接
};

1、Qt::AutoConnection
        一般信号槽不会写第五个参数,其实使用的默认值,使用这个值则连接类型会在信号发送时决定。
        如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。
        如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
2、Qt::DirectConnection
        槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程,有点类似于回调函数。
        效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。

3、Qt::QueuedConnection:
        槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。

4、Qt::BlockingQueuedConnection
        槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。使用该连接方式,接收者和发送者绝对不能在一个线程,否则程序会死锁!!!在多线程间需要同步的场合可能需要这个。

5、Qt::UniqueConnection
        这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。


二、怎样使用信号和槽机制?


        在使用信号与槽的类中,必须在类的定义中加入宏 Q_OBJECT;
        当一个信号被发射时,与其关联的槽函数通常被立即执行,就像正常调用一个函数一样。只有当信号关联的所有槽函数执行完毕后,才会执行发射信号处后面的代码。

信号和槽是什么?

在类中定义两个信号和两个槽

#pragma once

#include <QtWidgets/QWidget>
#include "ui_qttest.h"

class qttest : public QWidget
{
	Q_OBJECT

public:
	qttest(QWidget *parent = Q_NULLPTR);
signals:
	void signal_test1();
	void signal_test2(int index);
public slots:
	void slot_test1() {};
	void slot_test2(int index) {};
private:
	Ui::qttestClass ui;
};

构造函数中写个connnect:

#include "qttest.h"

qttest::qttest(QWidget *parent)
	: QWidget(parent)
{
	ui.setupUi(this);

	connect(this,&qttest::signal_test1,this,&qttest::slot_test1);//qt5
	connect(this, SIGNAL(signal_test1()) , this, SLOT(slot_test1()));//qt4
}

在这里,我使用qt5的信号与槽连接方式和qt4的信号与槽连接方式

通过SLOT 和SIGNAL的宏定义可以看出,qt4中信号是 :字符串1连接信号的函数名;槽是:字符串2连接函数名。

# define SLOT(a)     qFlagLocation("1"#a QLOCATION)
# define SIGNAL(a)   qFlagLocation("2"#a QLOCATION)

打开vs的预编译到文件:

重新编译后:

之后进入debug目录(我是debug编译)

打开qtttest.i文件,可以看到如下内容

qttest::qttest(QWidget *parent)
	: QWidget(parent)
{
	ui.setupUi(this);

	connect(this,&qttest::signal_test1,this,&qttest::slot_test1);
	connect(this, qFlagLocation("2""signal_test1()" "\0" "c:\\users\\admin\\source\\repos\\qttest\\qttest\\qttest.cpp" ":" "9") , this, qFlagLocation("1""slot_test1()" "\0" "c:\\users\\admin\\source\\repos\\qttest\\qttest\\qttest.cpp" ":" "9"));
}

      使用qt4方式的 信号与槽宏定义被展开成: 1/2 + 函数名 + 文件名  + 行号

连接方式1 SIGNAL SLOT

C:\Qt\5.9.8\Src\qtbase\src\corelib\kernel\qobject.h

    static QMetaObject::Connection connect(const QObject *sender, const char *signal,
                        const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);//连接方式1 SIGNAL SLOT

    static QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal,
                        const QObject *receiver, const QMetaMethod &method,
                        Qt::ConnectionType type = Qt::AutoConnection);//qt5

    inline QMetaObject::Connection connect(const QObject *sender, const char *signal,
                        const char *member, Qt::ConnectionType type = Qt::AutoConnection) const;//lamda

分析第一种连接方式的源码:

//sender 和receiver 必须是Qobject的对象,signal 和 medthod 是字符串  连接类型默认Auto
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
                                     const QObject *receiver, const char *method,
                                     Qt::ConnectionType type)
{
    // 1 检查指针是否为空
    if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
        qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
                 sender ? sender->metaObject()->className() : "(null)",
                 (signal && *signal) ? signal+1 : "(null)",
                 receiver ? receiver->metaObject()->className() : "(null)",
                 (method && *method) ? method+1 : "(null)");
        return QMetaObject::Connection(0);
    }
    QByteArray tmp_signal_name;

    // 2 检查宏指令  : 信号字符串中是否含有 "2"
    if (!check_signal_macro(sender, signal, "connect", "bind"))
        return QMetaObject::Connection(0);
    const QMetaObject *smeta = sender->metaObject();//调用信号对象的 metaObject
    const char *signal_arg = signal;
    ++signal; //skip code  跳过 字符串2 获取信号名称和参数类型
    QArgumentTypeArray signalTypes;//qt 的可变长数组
    Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7);//检测moc 版本
    QByteArray signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
    //在信号所在的静态元对象中找到信号的相对索引
    int signal_index = QMetaObjectPrivate::indexOfSignalRelative(
            &smeta, signalName, signalTypes.size(), signalTypes.constData());
    //进行容错检查
    if (signal_index < 0) {
        // check for normalized signatures
        tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
        signal = tmp_signal_name.constData() + 1;

        signalTypes.clear();
        signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
        smeta = sender->metaObject();
        signal_index = QMetaObjectPrivate::indexOfSignalRelative(
                &smeta, signalName, signalTypes.size(), signalTypes.constData());
    }
    if (signal_index < 0) {
        err_method_notfound(sender, signal_arg, "connect");
        err_info_about_objects("connect", sender, receiver);
        return QMetaObject::Connection(0);
    }
    signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
    signal_index += QMetaObjectPrivate::signalOffset(smeta);
   

   //3 对槽函数执行和信号相同的检测逻辑
    QByteArray tmp_method_name;
    int membcode = extract_code(method);

    if (!check_method_code(membcode, receiver, method, "connect"))
        return QMetaObject::Connection(0);
    const char *method_arg = method;
    ++method; // skip code

    QArgumentTypeArray methodTypes;
    QByteArray methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
    const QMetaObject *rmeta = receiver->metaObject();
    int method_index_relative = -1;
    Q_ASSERT(QMetaObjectPrivate::get(rmeta)->revision >= 7);
    switch (membcode) {
    case QSLOT_CODE:
        method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
                &rmeta, methodName, methodTypes.size(), methodTypes.constData());
        break;
    case QSIGNAL_CODE:
        method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
                &rmeta, methodName, methodTypes.size(), methodTypes.constData());
        break;
    }
    if (method_index_relative < 0) {
        // check for normalized methods
        tmp_method_name = QMetaObject::normalizedSignature(method);
        method = tmp_method_name.constData();

        methodTypes.clear();
        methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
        // rmeta may have been modified above
        rmeta = receiver->metaObject();
        switch (membcode) {
        case QSLOT_CODE:
            method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
                    &rmeta, methodName, methodTypes.size(), methodTypes.constData());
            break;
        case QSIGNAL_CODE:
            method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
                    &rmeta, methodName, methodTypes.size(), methodTypes.constData());
            break;
        }
    }

    if (method_index_relative < 0) {
        err_method_notfound(receiver, method_arg, "connect");
        err_info_about_objects("connect", sender, receiver);
        return QMetaObject::Connection(0);
    }

    //4 检查信号和槽的参数列表是否匹配
    if (!QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(),
                                              methodTypes.size(), methodTypes.constData())) {
        qWarning("QObject::connect: Incompatible sender/receiver arguments"
                 "\n        %s::%s --> %s::%s",
                 sender->metaObject()->className(), signal,
                 receiver->metaObject()->className(), method);
        return QMetaObject::Connection(0);
    }

   //5 检查信号与槽的链接方式
    int *types = 0;
    if ((type == Qt::QueuedConnection)
            && !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) {
        return QMetaObject::Connection(0);
    }

#ifndef QT_NO_DEBUG
    QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
    QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
    check_and_warn_compat(smeta, smethod, rmeta, rmethod);
#endif
    // 6 将信号与槽进行链接
    QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(
        sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));
    return handle;
}

1 断点调试,参数中的信号和槽的内容

在第一种链接方式中,信号和槽函数被处理成字符串

                   

2  check_signal_macro(sender, signal, "connect", "bind")  用于判断singal是不是以2开头:

static bool check_signal_macro(const QObject *sender, const char *signal,
                                const char *func, const char *op)
{
    int sigcode = extract_code(signal);
    if (sigcode != QSIGNAL_CODE) {
        if (sigcode == QSLOT_CODE)
            qWarning("QObject::%s: Attempt to %s non-signal %s::%s",
                     func, op, sender->metaObject()->className(), signal+1);
        else
            qWarning("QObject::%s: Use the SIGNAL macro to %s %s::%s",
                     func, op, sender->metaObject()->className(), signal);
        return false;
    }
    return true;
}

static int extract_code(const char *member)
{
    // extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE
    return (((int)(*member) - '0') & 0x3);
}

3 获取信号的静态元对象   const QMetaObject *smeta = sender->metaObject();

会获得对象生成的moc_qttest.cpp 文件中生成元对象

 静态元对象是通过定义Q_OBJECT宏生成的

staticMetaObject中的具体内容参见:qt 元对象 和moc原理_~怎么回事啊~的博客-CSDN博客 

 获取信号的名称和在元对象中的偏移:

     // 6 将信号与槽进行链接

/*!
    \internal
   Same as the QMetaObject::connect, but \a signal_index must be the result of QObjectPrivate::signalIndex

    method_index is relative to the rmeta metaobject, if rmeta is null, then it is absolute index

    the QObjectPrivate::Connection* has a refcount of 2, so it must be passed to a QMetaObject::Connection
 */

// sender 发送者  signal_index 信号偏移量  smeta 信号元对象
// receiver 接送者  method_index 槽函数偏移量  rmeta 槽函数元对象
QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
                                 int signal_index, const QMetaObject *smeta,
                                 const QObject *receiver, int method_index,
                                 const QMetaObject *rmeta, int type, int *types)
{
   //1 去除 sender 和receiver 的const 限制 使其可以读写
    QObject *s = const_cast<QObject *>(sender);
    QObject *r = const_cast<QObject *>(receiver);

   //2 在槽函数的元对象中提取槽方法的偏移量:32
    int method_offset = rmeta ? rmeta->methodOffset() : 0;
    Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);

    //3 获取槽 元对象中的回调  
    QObjectPrivate::StaticMetaCallFunction callFunction =
        rmeta ? rmeta->d.static_metacall : 0;


   //4 在对信号和槽的数据进行操作之前,首先上锁保证线程安全
    QOrderedMutexLocker locker(signalSlotLock(sender),
                               signalSlotLock(receiver));

   // 5 如果连接类型是只连接一次
    if (type & Qt::UniqueConnection) {
        QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;//获取发送者的信号存储容器
        //判断当前信号是否有效
        if (connectionLists && connectionLists->count() > signal_index) {
            const QObjectPrivate::Connection *c2 =
                (*connectionLists)[signal_index].first;//如果有效,获取到该信号的二级存储容器

            int method_index_absolute = method_index + method_offset;//获取槽方法的绝对索引

           //将槽方法的绝对索引在当前信号中排除是否重复已有该槽函数
            while (c2) {
                if (!c2->isSlotObject && c2->receiver == receiver && c2->method() == method_index_absolute)
                    return 0;
                c2 = c2->nextConnectionList;
            }
        }
        type &= Qt::UniqueConnection - 1;
    }

    //6 新建信号二级容器对象 并填充数据
    QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
    c->sender = s;
    c->signal_index = signal_index;
    c->receiver = r;
    c->method_relative = method_index;
    c->method_offset = method_offset;
    c->connectionType = type;
    c->isSlotObject = false;
    c->argumentTypes.store(types);
    c->nextConnectionList = 0;
    c->callFunction = callFunction;

    //7 将此容器存储到发送者的当前信号的槽函数存储列表中
    QObjectPrivate::get(s)->addConnection(signal_index, c.data());

    locker.unlock();
    QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
    if (smethod.isValid())
        s->connectNotify(smethod);

    return c.take();
}

    //3 获取槽 元对象中的回调  

即:

 里面存有信号和槽的方法调用:

 

 函数指针和lambda表达式链接方式

//Connect a signal to a pointer to qobject member function
//1 发送者和接收者的 类型 const typename QtPrivate::FunctionPointer<Func1>::Object 
     信号和槽的类型  
    template <typename Func1, typename Func2>
    static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
                                     const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,
                                     Qt::ConnectionType type = Qt::AutoConnection)
    {

       
        typedef QtPrivate::FunctionPointer<Func1> SignalType;
        typedef QtPrivate::FunctionPointer<Func2> SlotType;

        Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
                          "No Q_OBJECT in the class with the signal");


        //2 对提取的信号和槽的返回值类型和参数列表进行检查
        //compilation error if the arguments does not match.
        Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
                          "The slot requires more arguments than the signal provides.");
        Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
                          "Signal and slot arguments are not compatible.");
        Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
                          "Return type of the slot is not compatible with the return type of the signal.");


       // 3 判断连接类型
        const int *types = Q_NULLPTR;
        if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
            types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();


       //4 调用connectImpl
        return connectImpl(sender, reinterpret_cast<void **>(&signal),
                           receiver, reinterpret_cast<void **>(&slot),
                           new QtPrivate::QSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
                                           typename SignalType::ReturnType>(slot),
                            type, types, &SignalType::Object::staticMetaObject);
    }

 1 发送者和接收者的 类型 const typename QtPrivate::FunctionPointer<Func1>::Object 
     信号和槽的类型 

FunctionPointer作用:提取和保存信号和槽函数的数据信息

    template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...)>
    {
        typedef Obj Object;//函数地址
        typedef List<Args...>  Arguments;//形参列表
        typedef Ret ReturnType;//返回值类型
        typedef Ret (Obj::*Function) (Args...);//函数指针
        //计算形参个数,是否是指针类型
        enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true};
        template <typename SignalArgs, typename R>
        static void call(Function f, Obj *o, void **arg) {
            FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg);
        }
    };

 sender的类型const typename QtPrivate::FunctionPointer<Func1>::Object *sender,Func1 是信号的函数地址;如    connect(this,&qttest::signal_test1,this,&qttest::slot_test1);

与方式1的区别:

1 connect的传递参数区别:是否在编译阶段就对信号和槽的数据进行检查,方式1没有检查(运行时期检查信号和槽的拼写,如果拼写错误则报错),方式二会进行类型检查,在获取到信号和槽的数据后立即进行形参和返回值类型的检查

  二级调度 调用 connectImpl

QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal,
                                             const QObject *receiver, void **slot,
                                             QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
                                             const int *types, const QMetaObject *senderMetaObject)
{

    //1 检查信号
    if (!signal) {
        qWarning("QObject::connect: invalid null parameter");
        if (slotObj)
            slotObj->destroyIfLastRef();
        return QMetaObject::Connection();
    }

    //2 信号索引检查其有效性
    int signal_index = -1;
    void *args[] = { &signal_index, signal };
    for (; senderMetaObject && signal_index < 0; senderMetaObject = senderMetaObject->superClass()) {
        senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
        if (signal_index >= 0 && signal_index < QMetaObjectPrivate::get(senderMetaObject)->signalCount)
            break;
    }

    //3 检查发送者元对象
    if (!senderMetaObject) {
        qWarning("QObject::connect: signal not found in %s", sender->metaObject()->className());
        slotObj->destroyIfLastRef();
        return QMetaObject::Connection(0);
    }

   //4 计算信号的绝对索引
    signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject);

   //5 调用接口
    return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject);
}

QSlotObjectBase  内部基类(接口),包含调用管理槽所需的函数

// internal base class (interface) containing functions required to call a slot managed by a pointer to function.
    class QSlotObjectBase {
        QAtomicInt m_ref;//引用计数
        // don't use virtual functions here; we don't want the
        // compiler to create tons of per-polymorphic-class stuff that
        // we'll never need. We just use one function pointer.
        typedef void (*ImplFn)(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret);//函数指针 也就是void qttest::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
        const ImplFn m_impl;
    protected:
        enum Operation {
            Destroy,
            Call,
            Compare,

            NumOperations
        };
    public:
        //构造函数对引用结束和回调函数进行初始化
        explicit QSlotObjectBase(ImplFn fn) : m_ref(1), m_impl(fn) {}

        inline int ref() Q_DECL_NOTHROW { return m_ref.ref(); }
        inline void destroyIfLastRef() Q_DECL_NOTHROW
        { if (!m_ref.deref()) m_impl(Destroy, this, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR); }

        inline bool compare(void **a) { bool ret = false; m_impl(Compare, this, Q_NULLPTR, a, &ret); return ret; }
        inline void call(QObject *r, void **a)  { m_impl(Call,    this, r, a, Q_NULLPTR); }
    protected:
        ~QSlotObjectBase() {}
    private:
        Q_DISABLE_COPY(QSlotObjectBase)
    };


//子类:
    // implementation of QSlotObjectBase for which the slot is a pointer to member function of a QObject
    // Args and R are the List of arguments and the returntype of the signal to which the slot is connected.
    template<typename Func, typename Args, typename R> class QSlotObject : public QSlotObjectBase
    {
        typedef QtPrivate::FunctionPointer<Func> FuncType;
        Func function;
        static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
        {
            switch (which) {
            case Destroy:
                delete static_cast<QSlotObject*>(this_);
                break;
            case Call:
                FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a);
                break;
            case Compare:
                *ret = *reinterpret_cast<Func *>(a) == static_cast<QSlotObject*>(this_)->function;
                break;
            case NumOperations: ;
            }
        }
    public:
        explicit QSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {}
    };

第一项:Func2 槽函数函数指针

第二项 :typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value    信号的参数列表  ,槽函数的参数数量

第三项:typename SignalType::ReturnType  信号的返回类型和槽函数的返回类型

 QSlotObjectBase 是一个普通基类,而不是一个虚基类,在信号与槽连接调用connect 过程中,会new 出来一个QSlotOject对象,继承于QSlotObjectBase;在实际使用中,必然存在大量QSlotOject对象,如果重写QSlotObjectBase的方法,则会有大量的虚方法表,浪费内存。

三级调度

QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index,
                                             const QObject *receiver, void **slot,
                                             QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
                                             const int *types, const QMetaObject *senderMetaObject)
{
    //1 检查
    if (!sender || !receiver || !slotObj || !senderMetaObject) {
        qWarning("QObject::connect: invalid null parameter");
        if (slotObj)
            slotObj->destroyIfLastRef();
        return QMetaObject::Connection();
    }

    //2 去除const 属性
    QObject *s = const_cast<QObject *>(sender);
    QObject *r = const_cast<QObject *>(receiver);

    QOrderedMutexLocker locker(signalSlotLock(sender),
                               signalSlotLock(receiver));

    if (type & Qt::UniqueConnection && slot) {
        QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
        if (connectionLists && connectionLists->count() > signal_index) {
            const QObjectPrivate::Connection *c2 =
                (*connectionLists)[signal_index].first;

            while (c2) {
                if (c2->receiver == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) {
                    slotObj->destroyIfLastRef();
                    return QMetaObject::Connection();
                }
                c2 = c2->nextConnectionList;
            }
        }
        type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection);
    }

    QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
    c->sender = s;
    c->signal_index = signal_index;
    c->receiver = r;
    c->slotObj = slotObj;
    c->connectionType = type;
    c->isSlotObject = true;
    if (types) {
        c->argumentTypes.store(types);
        c->ownArgumentTypes = false;
    }

    QObjectPrivate::get(s)->addConnection(signal_index, c.data());
    QMetaObject::Connection ret(c.take());
    locker.unlock();

    QMetaMethod method = QMetaObjectPrivate::signal(senderMetaObject, signal_index);
    Q_ASSERT(method.isValid());
    s->connectNotify(method);

    return ret;
}

与方式1基本相同,唯一的区别:

    c->slotObj = slotObj;

方式1 使用的是callFunction;现在使用的是slotObj 

lambda表达式的实现方式

    static inline typename std::enable_if<QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, QMetaObject::Connection>::type
            connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot)
    {
        return connect(sender, signal, sender, slot, Qt::DirectConnection);
    }

调用了connect(sender, signal, sender, slot, Qt::DirectConnection);注意是 Qt::DirectConnection

之后的过程与函数指针基本相同

信号槽中的数据存储

问题1 :每个QObject及其派生类对象是否在创建之初就自带一个信号与槽的存储容器?它在哪里?具体是什么

C:\Qt\5.9.8\Src\qtbase\src\corelib\kernel\qobject.cpp

/*!
    \internal

    Internal version of connect used by the template version of QObject::connect (called via connectImpl) and
    also used by the QObjectPrivate::connect version used by QML. The signal_index is expected to be relative
    to the number of signals.
 */
QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index,
                                             const QObject *receiver, void **slot,
                                             QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
                                             const int *types, const QMetaObject *senderMetaObject)
{
    if (!sender || !receiver || !slotObj || !senderMetaObject) {
        qWarning("QObject::connect: invalid null parameter");
        if (slotObj)
            slotObj->destroyIfLastRef();
        return QMetaObject::Connection();
    }

    QObject *s = const_cast<QObject *>(sender);
    QObject *r = const_cast<QObject *>(receiver);

    QOrderedMutexLocker locker(signalSlotLock(sender),
                               signalSlotLock(receiver));

    if (type & Qt::UniqueConnection && slot) {
        QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
        if (connectionLists && connectionLists->count() > signal_index) {
            const QObjectPrivate::Connection *c2 =
                (*connectionLists)[signal_index].first;

            while (c2) {
                if (c2->receiver == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) {
                    slotObj->destroyIfLastRef();
                    return QMetaObject::Connection();
                }
                c2 = c2->nextConnectionList;
            }
        }
        type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection);
    }

    QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
    c->sender = s;
    c->signal_index = signal_index;
    c->receiver = r;
    c->slotObj = slotObj;
    c->connectionType = type;
    c->isSlotObject = true;
    if (types) {
        c->argumentTypes.store(types);
        c->ownArgumentTypes = false;
    }

    QObjectPrivate::get(s)->addConnection(signal_index, c.data());
    QMetaObject::Connection ret(c.take());
    locker.unlock();

    QMetaMethod method = QMetaObjectPrivate::signal(senderMetaObject, signal_index);
    Q_ASSERT(method.isValid());
    s->connectNotify(method);

    return ret;
}

信号存储容器的获取:       

QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;

QObjectPrivate继承QObjectData

任何继承于QObject 的类天生就有个QObjectPrivate成员对象

    static QObjectPrivate *get(QObject *o) {
        return o->d_func();
    }

 QObjectPrivate的get方法调用的是对象的d_func();即获取对象的d指针(关于d指针的介绍:qt d指针和对象树_~怎么回事啊~的博客-CSDN博客);

        因此:每个QObject对象及其派生类对象在创建之初就自带一个信号与槽的存储容器,它存在当前对象的d指针所指向的QObjectConnectionListVector的实例对象中,它是一个vector类型的存储容器。

问题2:对象关系,上至QObject是怎么关联的?

        QObjectPrivate继承QObjectData

        QObject存储有    QScopedPointer<QObjectData> d_ptr;并在构造函数中指向派生类QObjectPrivate的实例对象,在QObjectPrivate中存储有    QObjectConnectionListVector *connectionLists;为实际的数据存储容器

        QObjectPrivate和QObject属于一种impl 模式,QObjectPrivate中保存这QObject的基本数据,只有QObject可以访问从而实现对数据的保护。QObject实现了大量的public方法,这两个类可以在内部相互访问

       

问题3:是怎么进行存储的?具体存储容器是什么?

C:\Qt\5.9.8\Src\qtbase\src\corelib\kernel\qobject.cpp

        class QObjectConnectionListVector : public QVector<QObjectPrivate::ConnectionList>

实质上QObjectConnectionListVector是一个QVector的容器,存储的元素是ConnectionList类型。

C:\Qt\5.9.8\Src\qtbase\src\corelib\kernel\qobject_p.h

    // ConnectionList is a singly-linked list
    struct ConnectionList {
        ConnectionList() : first(0), last(0) {}
        Connection *first;
		Connection *last;
    };

ConnectionList  是一个链表头,指向一个双向链表,数据节点是Connection,Connection是一个双向链表,

  struct Connection
    {
        QObject *sender;//发送者对象
        QObject *receiver;//接收者对象

        //回调函数,qt4是  calFunction qt5是slotObj
        union {
            StaticMetaCallFunction callFunction;
            QtPrivate::QSlotObjectBase *slotObj;
        };
        // The next pointer for the singly-linked ConnectionList
        Connection *nextConnectionList;//下一个节点的指针
        //senders linked list
        Connection *next;
        Connection **prev;
        QAtomicPointer<const int> argumentTypes;//参数值数组指针
        QAtomicInt ref_;
        ushort method_offset;//方法偏移量
        ushort method_relative;//方法的相对索引
        //信号的绝对索引
        uint signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())
        //信号与槽的链接方式
        ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
        ushort isSlotObject : 1;
        ushort ownArgumentTypes : 1;
        Connection() : nextConnectionList(0), ref_(2), ownArgumentTypes(true) {
            //ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection
        }
        ~Connection();
        int method() const { Q_ASSERT(!isSlotObject); return method_offset + method_relative; }
        void ref() { ref_.ref(); }
        void deref() {
            if (!ref_.deref()) {
                Q_ASSERT(!receiver);
                delete this;
            }
        }
    };

回调函数StaticMetaCallFunction(类型:    typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);)即静态元对象中第四个参数:

里面存储了对信号和槽的具体调用

 


/*
    This vector contains the all connections from an object.

    Each object may have one vector containing the lists of
    connections for a given signal. The index in the vector correspond
    to the signal index. The signal index is the one returned by
    QObjectPrivate::signalIndex (not QMetaObject::indexOfSignal).
    Negative index means connections to all signals.

    This vector is protected by the object mutex (signalSlotMutexes())

    Each Connection is also part of a 'senders' linked list. The mutex
    of the receiver must be locked when touching the pointers of this
    linked list.
*/
class QObjectConnectionListVector : public QVector<QObjectPrivate::ConnectionList>
{
public:
    //当前对象的使用状态
    bool orphaned; //the QObject owner of this vector has been destroyed while the vector was inUse
    //断开的链接对象是否已经从容器中移除
    bool dirty; //some Connection have been disconnected (their receiver is 0) but not removed from the list yet
    int inUse; //number of functions that are currently accessing this object or its connections
    QObjectPrivate::ConnectionList allsignals;

    QObjectConnectionListVector()
        : QVector<QObjectPrivate::ConnectionList>(), orphaned(false), dirty(false), inUse(0)
    { }

    QObjectPrivate::ConnectionList &operator[](int at)
    {
        if (at < 0)
            return allsignals;
        return QVector<QObjectPrivate::ConnectionList>::operator[](at);
    }
};

创建一个Connection并添加到信号索引为signalindex的双向链表中

/*!
  \internal
  Add the connection \a c to the list of connections of the sender's object
  for the specified \a signal

  The signalSlotLock() of the sender and receiver must be locked while calling
  this function

  Will also add the connection in the sender's list of the receiver.
 */
void QObjectPrivate::addConnection(int signal, Connection *c)
{
    //1 检查connectionLists 是否存在,不存在则创建
    Q_ASSERT(c->sender == q_ptr);
    if (!connectionLists)
        connectionLists = new QObjectConnectionListVector();

    //2 检查信号的绝对索引位置是否存在,不存在则扩充vector
    if (signal >= connectionLists->count())
        connectionLists->resize(signal + 1);


    //3 找到该信号所对应的双向链表,并且将新的Connection追加至尾部
    ConnectionList &connectionList = (*connectionLists)[signal];
    if (connectionList.last) {
        connectionList.last->nextConnectionList = c;//
    } else {
        connectionList.first = c;
    }
    connectionList.last = c;

   //
    cleanConnectionLists();

    //4  将Connection挂载到双向链表中,并重设指针指向位置
    c->prev = &(QObjectPrivate::get(c->receiver)->senders);
    c->next = *c->prev;
    *c->prev = c;
    if (c->next)
        c->next->prev = &c->next;

    if (signal < 0) {
        connectedSignals[0] = connectedSignals[1] = ~0;
    } else if (signal < (int)sizeof(connectedSignals) * 8) {
        connectedSignals[signal >> 5] |= (1 << (signal & 0x1f));
    }
}

QMetaObject的调用时机和使用方式

 

        元对象主要包括QMetaObject和   QMetaObjectPrivate,同时包含一个数据结合体Connection;

        元对象的主要作用:存储当前类对象的元对象数据以及对外提供获取接口,主要对类信息和类信息索引进行操作

        QMetaObject的功能

1 对元对象信息进行操作:[偏移量]、[数量]、[索引]、通过索引获取对应数据对象、调用对象成员函数,信号激活,(指针,lambda)方法检测

2 存储元对象信息

        QMetaObject::Connection  是一个包含所有数据的私有数据结构体

        QMetaObjectPrivate:

1 存储类信息

2 获取静态元对象

3 分别获取索引/偏移量/ 绝对 /顺序索引、检查信号的参数信息、参数类型签名

4 执行链接

 connect 函数的返回值是一个QMetaObject::Connection类型

QMetaObject:

struct Q_CORE_EXPORT QMetaObject
{
    class Connection;//内嵌类
    const char *className() const;//当前类的名称
    const QMetaObject *superClass() const;//父类的QMetaObject 指针

    bool inherits(const QMetaObject *metaObject) const Q_DECL_NOEXCEPT;
    QObject *cast(QObject *obj) const;
    const QObject *cast(const QObject *obj) const;

#ifndef QT_NO_TRANSLATION
    QString tr(const char *s, const char *c, int n = -1) const;
#endif // QT_NO_TRANSLATION

    int methodOffset() const;//方法偏移量获取
    int enumeratorOffset() const;//枚举偏移量获取
    int propertyOffset() const;//属性偏移量获取
    int classInfoOffset() const;//类信息偏移量获取

    //获取个数
    int constructorCount() const;
    int methodCount() const;
    int enumeratorCount() const;
    int propertyCount() const;
    int classInfoCount() const;

    //获取索引
    int indexOfConstructor(const char *constructor) const;
    int indexOfMethod(const char *method) const;
    int indexOfSignal(const char *signal) const;
    int indexOfSlot(const char *slot) const;
    int indexOfEnumerator(const char *name) const;
    int indexOfProperty(const char *name) const;
    int indexOfClassInfo(const char *name) const;

    //通过索引获取对应的数据对象
    QMetaMethod constructor(int index) const;
    QMetaMethod method(int index) const;
    QMetaEnum enumerator(int index) const;
    QMetaProperty property(int index) const;
    QMetaClassInfo classInfo(int index) const;
    QMetaProperty userProperty() const;


    //检查信号和槽函数的字符串
    static bool checkConnectArgs(const char *signal, const char *method);
    //检查信号与槽的对象
    static bool checkConnectArgs(const QMetaMethod &signal,
                                 const QMetaMethod &method);
    static QByteArray normalizedSignature(const char *method);
    static QByteArray normalizedType(const char *type);

    // internal index-based connect
    static Connection connect(const QObject *sender, int signal_index,
                        const QObject *receiver, int method_index,
                        int type = 0, int *types = Q_NULLPTR);
    // internal index-based disconnect
    static bool disconnect(const QObject *sender, int signal_index,
                           const QObject *receiver, int method_index);
    static bool disconnectOne(const QObject *sender, int signal_index,
                              const QObject *receiver, int method_index);
    // internal slot-name based connect  通过槽函数名称链接
    static void connectSlotsByName(QObject *o);

    // internal index-based signal activation  激活
    static void activate(QObject *sender, int signal_index, void **argv);
    static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
    static void activate(QObject *sender, int signal_offset, int local_signal_index, void **argv);

    static bool invokeMethod(QObject *obj, const char *member,
                             Qt::ConnectionType,
                             QGenericReturnArgument ret,
                             QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
                             QGenericArgument val1 = QGenericArgument(),
                             QGenericArgument val2 = QGenericArgument(),
                             QGenericArgument val3 = QGenericArgument(),
                             QGenericArgument val4 = QGenericArgument(),
                             QGenericArgument val5 = QGenericArgument(),
                             QGenericArgument val6 = QGenericArgument(),
                             QGenericArgument val7 = QGenericArgument(),
                             QGenericArgument val8 = QGenericArgument(),
                             QGenericArgument val9 = QGenericArgument());

    static inline bool invokeMethod(QObject *obj, const char *member,
                             QGenericReturnArgument ret,
                             QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
                             QGenericArgument val1 = QGenericArgument(),
                             QGenericArgument val2 = QGenericArgument(),
                             QGenericArgument val3 = QGenericArgument(),
                             QGenericArgument val4 = QGenericArgument(),
                             QGenericArgument val5 = QGenericArgument(),
                             QGenericArgument val6 = QGenericArgument(),
                             QGenericArgument val7 = QGenericArgument(),
                             QGenericArgument val8 = QGenericArgument(),
                             QGenericArgument val9 = QGenericArgument())
    {
        return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3,
                val4, val5, val6, val7, val8, val9);
    }

    static inline bool invokeMethod(QObject *obj, const char *member,
                             Qt::ConnectionType type,
                             QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
                             QGenericArgument val1 = QGenericArgument(),
                             QGenericArgument val2 = QGenericArgument(),
                             QGenericArgument val3 = QGenericArgument(),
                             QGenericArgument val4 = QGenericArgument(),
                             QGenericArgument val5 = QGenericArgument(),
                             QGenericArgument val6 = QGenericArgument(),
                             QGenericArgument val7 = QGenericArgument(),
                             QGenericArgument val8 = QGenericArgument(),
                             QGenericArgument val9 = QGenericArgument())
    {
        return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2,
                                 val3, val4, val5, val6, val7, val8, val9);
    }

    static inline bool invokeMethod(QObject *obj, const char *member,
                             QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
                             QGenericArgument val1 = QGenericArgument(),
                             QGenericArgument val2 = QGenericArgument(),
                             QGenericArgument val3 = QGenericArgument(),
                             QGenericArgument val4 = QGenericArgument(),
                             QGenericArgument val5 = QGenericArgument(),
                             QGenericArgument val6 = QGenericArgument(),
                             QGenericArgument val7 = QGenericArgument(),
                             QGenericArgument val8 = QGenericArgument(),
                             QGenericArgument val9 = QGenericArgument())
    {
        return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0,
                val1, val2, val3, val4, val5, val6, val7, val8, val9);
    }

    QObject *newInstance(QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
                         QGenericArgument val1 = QGenericArgument(),
                         QGenericArgument val2 = QGenericArgument(),
                         QGenericArgument val3 = QGenericArgument(),
                         QGenericArgument val4 = QGenericArgument(),
                         QGenericArgument val5 = QGenericArgument(),
                         QGenericArgument val6 = QGenericArgument(),
                         QGenericArgument val7 = QGenericArgument(),
                         QGenericArgument val8 = QGenericArgument(),
                         QGenericArgument val9 = QGenericArgument()) const;
    ///当前qobject对象的操作功能 在qt_static_metacall中使用
    enum Call {
        InvokeMetaMethod,
        ReadProperty,
        WriteProperty,
        ResetProperty,
        QueryPropertyDesignable,
        QueryPropertyScriptable,
        QueryPropertyStored,
        QueryPropertyEditable,
        QueryPropertyUser,
        CreateInstance,
        IndexOfMethod,
        RegisterPropertyMetaType,
        RegisterMethodArgumentMetaType
    };

    int static_metacall(Call, int, void **) const;
    static int metacall(QObject *, Call, int, void **);

    struct { // private data
        const QMetaObject *superdata;//父类的qmetaobject的实例指针
        const QByteArrayData *stringdata;//当前类信息的string字符串
        const uint *data;//类信息的索引表
        typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);//具体信号槽的调用
        StaticMetacallFunction static_metacall;
        const QMetaObject * const *relatedMetaObjects;
        void *extradata; //reserved for future use
    } d;
};

QMetaObject::Connection

class Q_CORE_EXPORT QMetaObject::Connection {
    void *d_ptr; //QObjectPrivate::Connection*
    explicit Connection(void *data) : d_ptr(data) {  }

    //友元类
    friend class QObject;
    friend class QObjectPrivate;
    friend struct QMetaObject;
    bool isConnected_helper() const;
public:
    ~Connection();
    Connection();
    Connection(const Connection &other);
    Connection &operator=(const Connection &other);
#ifdef Q_QDOC
    operator bool() const;
#else
    typedef void *Connection::*RestrictedBool;
    operator RestrictedBool() const { return d_ptr && isConnected_helper() ? &Connection::d_ptr : Q_NULLPTR; }
#endif

#ifdef Q_COMPILER_RVALUE_REFS
    inline Connection(Connection &&o) : d_ptr(o.d_ptr) { o.d_ptr = Q_NULLPTR; }
    inline Connection &operator=(Connection &&other)
    { qSwap(d_ptr, other.d_ptr); return *this; }
#endif
};

QMetaObject::Connection 中 void *d_ptr;是QMetaObjectPrivate类型,用于存储数据


struct QMetaObjectPrivate
{
    // revision 7 is Qt 5.0 everything lower is not supported
    enum { OutputRevision = 7 }; // 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;

    //对外提供获取类对象的元对象信息接口
    static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject)
    { return reinterpret_cast<const QMetaObjectPrivate*>(metaobject->d.data); }

    static int originalClone(const QMetaObject *obj, int local_method_index);

    static QByteArray decodeMethodSignature(const char *signature,
                                            QArgumentTypeArray &types);

    //对象信息的索引获取和偏移量获取
    static int indexOfSignalRelative(const QMetaObject **baseObject,
                                     const QByteArray &name, int argc,
                                     const QArgumentType *types);
    static int indexOfSlotRelative(const QMetaObject **m,
                                   const QByteArray &name, int argc,
                                   const QArgumentType *types);
    static int indexOfSignal(const QMetaObject *m, const QByteArray &name,
                             int argc, const QArgumentType *types);
    static int indexOfSlot(const QMetaObject *m, const QByteArray &name,
                           int argc, const QArgumentType *types);
    static int indexOfMethod(const QMetaObject *m, const QByteArray &name,
                             int argc, const QArgumentType *types);
    static int indexOfConstructor(const QMetaObject *m, const QByteArray &name,
                                  int argc, const QArgumentType *types);
    Q_CORE_EXPORT static QMetaMethod signal(const QMetaObject *m, int signal_index);
    Q_CORE_EXPORT static int signalOffset(const QMetaObject *m);
    Q_CORE_EXPORT static int absoluteSignalCount(const QMetaObject *m);
    Q_CORE_EXPORT static int signalIndex(const QMetaMethod &m);
    static bool checkConnectArgs(int signalArgc, const QArgumentType *signalTypes,
                                 int methodArgc, const QArgumentType *methodTypes);
    static bool checkConnectArgs(const QMetaMethodPrivate *signal,
                                 const QMetaMethodPrivate *method);

    static QList<QByteArray> parameterTypeNamesFromSignature(const char *signature);

#ifndef QT_NO_QOBJECT
    //defined in qobject.cpp
    enum DisconnectType { DisconnectAll, DisconnectOne };
    static void memberIndexes(const QObject *obj, const QMetaMethod &member,
                              int *signalIndex, int *methodIndex);


    //将新创建的链接添加到信号索引对应的双向链表中
    static QObjectPrivate::Connection *connect(const QObject *sender, int signal_index,
                        const QMetaObject *smeta,
                        const QObject *receiver, int method_index_relative,
                        const QMetaObject *rmeta = 0,
                        int type = 0, int *types = 0);
    static bool disconnect(const QObject *sender, int signal_index,
                           const QMetaObject *smeta,
                           const QObject *receiver, int method_index, void **slot,
                           DisconnectType = DisconnectAll);
    static inline bool disconnectHelper(QObjectPrivate::Connection *c,
                                        const QObject *receiver, int method_index, void **slot,
                                        QMutex *senderMutex, DisconnectType = DisconnectAll);
#endif
};

 信号的触发流程:

        如调用一个信号  :一般的写法是  emit signal_test2(2);翻看emit 宏定义 是0,则实质是调用moc文件中信号实现:

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

        调用了QMetaObject::activate方法。

C:\Qt\5.9.8\Src\qtbase\src\corelib\kernel\qobject.cpp

/*!
    \internal
 */
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
                           void **argv)
{
    activate(sender, QMetaObjectPrivate::signalOffset(m), local_signal_index, argv);
}

其中 :local_singal_index:表示信号的下标;也就是当前信号是moc文件中的第几个信号
QMetaObjectPrivate::signalOffset(m) 表示第一个信号在类信息中的偏移地址:

/*!
    \internal
    \since 5.0

    Returns the signal offset for the class \a m; i.e., the index position
    of the class's first signal.

    Similar to QMetaObject::methodOffset(), but non-signal methods are
    excluded.
*/
int QMetaObjectPrivate::signalOffset(const QMetaObject *m)
{
    Q_ASSERT(m != 0);
    int offset = 0;
    for (m = m->d.superdata; m; m = m->d.superdata)
        offset += priv(m->d.data)->signalCount;
    return offset;
}

真正的激活过程: QMetaObject::activate


/*!
    \internal
 */
void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)
{
    //1 计算当前信号绝对索引 = 信号偏移量 + 本地索引
    int signal_index = signalOffset + local_signal_index;

    // 检查信号是否处于阻塞状态
    if (sender->d_func()->blockSig)
        return;

    if (sender->d_func()->isDeclarativeSignalConnected(signal_index)
            && QAbstractDeclarativeData::signalEmitted) {
        QAbstractDeclarativeData::signalEmitted(sender->d_func()->declarativeData, sender,
                                                signal_index, argv);
    }

  
    //2 检查此信号是否存储信号槽链接
    if (!sender->d_func()->isSignalConnected(signal_index, /*checkDeclarative =*/ false)
        && !qt_signal_spy_callback_set.signal_begin_callback
        && !qt_signal_spy_callback_set.signal_end_callback) {
        // The possible declarative connection is done, and nothing else is connected, so:
        return;
    }

    void *empty_argv[] = { 0 };
    if (qt_signal_spy_callback_set.signal_begin_callback != 0) {
        qt_signal_spy_callback_set.signal_begin_callback(sender, signal_index,
                                                         argv ? argv : empty_argv);
    }


 
    {
    QMutexLocker locker(signalSlotLock(sender));
    struct ConnectionListsRef {
        QObjectConnectionListVector *connectionLists;
        ConnectionListsRef(QObjectConnectionListVector *connectionLists) : connectionLists(connectionLists)
        {
            if (connectionLists)
                ++connectionLists->inUse;
        }
        ~ConnectionListsRef()
        {
            if (!connectionLists)
                return;

            --connectionLists->inUse;
            Q_ASSERT(connectionLists->inUse >= 0);
            if (connectionLists->orphaned) {
                if (!connectionLists->inUse)
                    delete connectionLists;
            }
        }

        QObjectConnectionListVector *operator->() const { return connectionLists; }
    };
    ConnectionListsRef connectionLists = sender->d_func()->connectionLists;
    if (!connectionLists.connectionLists) {
        locker.unlock();
        if (qt_signal_spy_callback_set.signal_end_callback != 0)
            qt_signal_spy_callback_set.signal_end_callback(sender, signal_index);
        return;
    }

     //3 获取当前信号的绝对索引在connectionLists中所对应槽函数双向链表的链表头
    const QObjectPrivate::ConnectionList *list;
    if (signal_index < connectionLists->count())
        list = &connectionLists->at(signal_index);//获取链表头
    else
        list = &connectionLists->allsignals;

    Qt::HANDLE currentThreadId = QThread::currentThreadId();//获取当前信号的当前线程id


    // 4 遍历双向链表
    do {
        
        QObjectPrivate::Connection *c = list->first;
        if (!c) continue;
        // We need to check against last here to ensure that signals added
        // during the signal emission are not emitted in this emission.
        QObjectPrivate::Connection *last = list->last;

        do {
            if (!c->receiver)
                continue;

            QObject * const receiver = c->receiver;
         
            //检查接收者和发送者是否处于同一线程
            const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId.load();

            // determine if this connection should be sent immediately or
            // put into the event queue
            //判断信号槽的链接类型
            //4.1 如果是自动链接或队列链接  且不在同一个线程
            if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
                || (c->connectionType == Qt::QueuedConnection)) {
                queued_activate(sender, signal_index, c, argv ? argv : empty_argv, locker);
                continue;

           //4.2  如果是堵塞队列链接
#ifndef QT_NO_THREAD
            } else if (c->connectionType == Qt::BlockingQueuedConnection) {
                if (receiverInSameThread) {
                    qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "
                    "Sender is %s(%p), receiver is %s(%p)",
                    sender->metaObject()->className(), sender,
                    receiver->metaObject()->className(), receiver);
                }
                QSemaphore semaphore;
                QMetaCallEvent *ev = c->isSlotObject ?
                    new QMetaCallEvent(c->slotObj, sender, signal_index, 0, 0, argv ? argv : empty_argv, &semaphore) :
                    new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal_index, 0, 0, argv ? argv : empty_argv, &semaphore);
                QCoreApplication::postEvent(receiver, ev);
                locker.unlock();
                semaphore.acquire();
                locker.relock();
                continue;
#endif
            }

            QConnectionSenderSwitcher sw;
            
            //如果在同一个线程
            if (receiverInSameThread) {
                sw.switchSender(receiver, sender, signal_index);
            }
            
            //4.3 如果是函数地址链接方式
            if (c->isSlotObject) {
                c->slotObj->ref();
                QScopedPointer<QtPrivate::QSlotObjectBase, QSlotObjectBaseDeleter> obj(c->slotObj);
                locker.unlock();
                obj->call(receiver, argv ? argv : empty_argv);

                // Make sure the slot object gets destroyed before the mutex is locked again, as the
                // destructor of the slot object might also lock a mutex from the signalSlotLock() mutex pool,
                // and that would deadlock if the pool happens to return the same mutex.
                obj.reset();

                locker.relock();
            //4.4 如果是 SINGAL  SLOT链接方式   判断槽偏移量是否小于接收者元对象中的方法偏移量
            } else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
                //we compare the vtable to make sure we are not in the destructor of the object.
                const int methodIndex = c->method();//方法索引
                const int method_relative = c->method_relative; //相对索引
                const auto callFunction = c->callFunction;//回调函数
                locker.unlock();
                if (qt_signal_spy_callback_set.slot_begin_callback != 0)
                    qt_signal_spy_callback_set.slot_begin_callback(receiver, methodIndex, argv ? argv : empty_argv);
                //传入参数 ,调用回调函数 从而实现对信号绑定槽函数的具体调用 就是  staticMetaObject 中的  qt_static_metacall
                callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);

                if (qt_signal_spy_callback_set.slot_end_callback != 0)
                    qt_signal_spy_callback_set.slot_end_callback(receiver, methodIndex);
                locker.relock();
            } else {
                const int method = c->method_relative + c->method_offset;
                locker.unlock();

                if (qt_signal_spy_callback_set.slot_begin_callback != 0) {
                    qt_signal_spy_callback_set.slot_begin_callback(receiver,
                                                                method,
                                                                argv ? argv : empty_argv);
                }

                metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);

                if (qt_signal_spy_callback_set.slot_end_callback != 0)
                    qt_signal_spy_callback_set.slot_end_callback(receiver, method);

                locker.relock();
            }

            if (connectionLists->orphaned)
                break;
        } while (c != last && (c = c->nextConnectionList) != 0);

        if (connectionLists->orphaned)
            break;
    } while (list != &connectionLists->allsignals &&
        //start over for all signals;
        ((list = &connectionLists->allsignals), true));

    }

    if (qt_signal_spy_callback_set.signal_end_callback != 0)
        qt_signal_spy_callback_set.signal_end_callback(sender, signal_index);

}

下面分情况讨论每种连接方式的具体槽函数的调用方式:

  如果是函数地址链接方式

            //4.3 如果是函数地址链接方式
            if (c->isSlotObject) {
                c->slotObj->ref();
                QScopedPointer<QtPrivate::QSlotObjectBase, QSlotObjectBaseDeleter> obj(c->slotObj);
                locker.unlock();
                //执行槽函数
                obj->call(receiver, argv ? argv : empty_argv);

                // Make sure the slot object gets destroyed before the mutex is locked again, as the
                // destructor of the slot object might also lock a mutex from the signalSlotLock() mutex pool,
                // and that would deadlock if the pool happens to return the same mutex.
                obj.reset();

                locker.relock();
         
            } 

调用obj->call函数  C:\Qt\5.9.8\msvc2015\include\QtCore\qobject_impl.h

inline void call(QObject *r, void **a)  { m_impl(Call,    this, r, a, Q_NULLPTR); }

// 调用impl 函数

        typedef QtPrivate::FunctionPointer<Func> FuncType;
        Func function;
        static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
        {
            switch (which) {
            case Destroy:
                delete static_cast<QSlotObject*>(this_);
                break;
            case Call:
                FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a);
                break;
            case Compare:
                *ret = *reinterpret_cast<Func *>(a) == static_cast<QSlotObject*>(this_)->function;
                break;
            case NumOperations: ;
            }
        }

//走到case Call  一层层的模版!!!

        static void call(Function f, Obj *o, void **arg) {
            FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg);
        }

    template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj>
    struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...)> {
        static void call(SlotRet (Obj::*f)(SlotArgs...), Obj *o, void **arg) {
            (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]);
        }
    };

调用到槽函数



 

如果是 SINGAL  SLOT链接方式

 

            //4.4 如果是 SINGAL  SLOT链接方式   判断槽偏移量是否小于接收者元对象中的方法偏移量
            } else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
                //we compare the vtable to make sure we are not in the destructor of the object.
                const int methodIndex = c->method();//方法索引
                const int method_relative = c->method_relative; //相对索引
                const auto callFunction = c->callFunction;//回调函数
                locker.unlock();
                if (qt_signal_spy_callback_set.slot_begin_callback != 0)
                    qt_signal_spy_callback_set.slot_begin_callback(receiver, methodIndex, argv ? argv : empty_argv);
                //传入参数 ,调用回调函数 从而实现对信号绑定槽函数的具体调用 就是  staticMetaObject 中的  qt_static_metacall
                callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);

                if (qt_signal_spy_callback_set.slot_end_callback != 0)
                    qt_signal_spy_callback_set.slot_end_callback(receiver, methodIndex);
                locker.relock();
            }

调用callFunction ,callFunction 是一个函数指针。调用接收者对象的:


void qttest::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        qttest *_t = static_cast<qttest *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->signal_test1(); break;
        case 1: _t->signal_test2((*reinterpret_cast< int(*)>(_a[1]))); break;
        case 2: _t->slot_test1(); break;
        case 3: _t->slot_test2((*reinterpret_cast< int(*)>(_a[1]))); break;
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        {
            typedef void (qttest::*_t)();
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&qttest::signal_test1)) {
                *result = 0;
                return;
            }
        }
        {
            typedef void (qttest::*_t)(int );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&qttest::signal_test2)) {
                *result = 1;
                return;
            }
        }
    }
}

找到槽函数的 索引并且调用:

 

  如果是自动链接或队列链接  且不在同一个线程

            if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
                || (c->connectionType == Qt::QueuedConnection)) {
                queued_activate(sender, signal_index, c, argv ? argv : empty_argv, locker);
                continue;
            }

调用queued_activate函数,创建QMetaCallEvent 并且使用postEvent发送到接收者线程中


/*!
    \internal

    \a signal must be in the signal index range (see QObjectPrivate::signalIndex()).
*/
static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv,
                            QMutexLocker &locker)
{
......

    //创建MetaCallEvent 并且使用postEvent发送到接收者线程中
    QMetaCallEvent *ev = c->isSlotObject ?
        new QMetaCallEvent(c->slotObj, sender, signal, nargs, types, args) :
        new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal, nargs, types, args);
    QCoreApplication::postEvent(c->receiver, ev);
}

QCoreApplication::postEvent 的相关使用参考qt-事件循环系统_~怎么回事啊~的博客-CSDN博客

postEvent将该事件发送到接受者的postEventList中并调用接收者的事件分发器dispatcher->wakeUp();唤醒接收者的事件循环,接受者的隐藏窗口过程函数qt_internal_proc 进行事件处理:,堆栈如下:

 最终调用到bool QObject::event(QEvent *e),执行

bool QObject::event(QEvent *e)
{
    switch (e->type()) {
......

    case QEvent::MetaCall:
        {
            QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e);

            QConnectionSenderSwitcher sw(this, const_cast<QObject*>(mce->sender()), mce->signalId());

            mce->placeMetaCall(this);
            break;
        }
......

接下来调用   slotObj_->call(object, args_); call是模版编程,接下来的模版编程我也没怎么看懂,最终调用到槽函数。

void QMetaCallEvent::placeMetaCall(QObject *object)
{
    if (slotObj_) {
        slotObj_->call(object, args_);
    } else if (callFunction_ && method_offset_ <= object->metaObject()->methodOffset()) {
        callFunction_(object, QMetaObject::InvokeMetaMethod, method_relative_, args_);
    } else {
        QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method_offset_ + method_relative_, args_);
    }
}

 

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值