QT信号槽分析

简介: QT信号槽分析

环境安装


默认安装好了VS2015 和QT

下载QT pdb和QT src压缩包(选择自己的版本,我是5.9.8)

qt下载网址



拷贝自己需要的文件,qt目录中D:\Qt\Qt5.9.8\5.9.8我是这个



并且把源码也拷贝进同一目录,当然其他的目录也行

设置VS选项

工具->选项->调试->符号



项目->xxxx属性->vc++目录(添加源码路径)



测试


对着qt的函数或者宏按F12



安装查看源码的软件(Source Insight)

这个工具可以很好的分析源码



分析QT信号槽


分析moc生成的文件

案例代码

#include <QObject>
class QtClass : public QObject
{
    Q_OBJECT
public:
    QtClass(QObject *parent);
    ~QtClass();
    void Test()
{
        emit Sig1(5);
    }
signals:
    void Sig1(int nVal);     //void Sig2(bool b);
    //void Sig3(bool b);
    //void Sig4(bool b, int nVal);
    private slots:
    void Slot1(int nVal0)
{
        qDebug() << nVal0;
    }
}

信号函数只有声明,没有实现?

emit Sig1(5);这句代码调用信号,但是我们没有实现信号这个函数呀。我们没有实现,那么是什么能够帮我们实现呢?当然是编译器。

调试信号函数

F11发现是有实现代码的吧



打开文件所在目录



查看moc文件夹,分析编译器都给我们生成了什么

定义了一个结构体

qt_meta_stringdata_QtClass_t


struct qt_meta_stringdata_QtClass_t {     QByteArrayData data[6];     char stringdata0[31];
};

后面创建了一个结构体静态变量里面存放一些类、参数、函数名等信息。这个第一个参数应该是ID,字符串的开始位置,字符串size

QT_MOC_LITERAL(0, 0, 7), // "QtClass"

static const qt_meta_stringdata_QtClass_t qt_meta_stringdata_QtClass = {     {
QT_MOC_LITERAL(0, 0, 7), // "QtClass" //类名
QT_MOC_LITERAL(1, 8, 4), // "Sig1"      //信号名
QT_MOC_LITERAL(2, 13, 0), // ""        
QT_MOC_LITERAL(3, 14, 4), // "nVal" //参数名
QT_MOC_LITERAL(4, 19, 5), // "Slot1" //槽函数名
QT_MOC_LITERAL(5, 25, 5) // "nVal0"     //槽参数名 
    },
    "QtClass\0Sig1\0\0nVal\0Slot1\0nVal0"
};

qt_meta_data_QtClass : 存储类中函数相关的信息


static const uint qt_meta_data_QtClass[] = {  // content:
       7,       // revision  //qt对应版本
       0,       // classname        0,    0, // classinfo
       2,   14, // methods   //2个函数 context长度14
       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 */,
//name:对应qt_meta_stringdata_QtClass_t结构体的ID,是"Sig1"
//argc:是参数个数
//parameters:该函数的具体声明在在qt_meta_data_QtClass结构体的偏移
 // slots: name, argc, parameters, tag, flags
       4,    1,   27,    2, 0x08 /* Private */,
//上面的parameters就是指向这里
// 返回值   参数1... 这个结构的大小
24 // signals: parameters
    QMetaType::Void, QMetaType::Int,    3,
27 // slots: parameters
    QMetaType::Void, QMetaType::Int,    5,
       0        // eod
};

qt_static_metacall类的信号/槽函数调用的实现地址判断传进来的ID然后调用函数


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

staticMetaObject : 类信息的总和

红色框是父类地址,蓝色是存储字符串的结构体的地址,黑色是存放信号槽函数信息的结构体,类的信号/槽函数调用的实现地址



metaObject:判断静态/动态调用



qt_metacast:返回类名称



+qt_metacall:函数调用

void** _a:数组指针, 每个指针指向一个函数的地址

_c:实现函数的类型

_id : 函数id, 判断是否实现 & 以何种方式实现该函数




信号函数的实现

void* _a[]是一个信号对应的槽函数的地址

一个信号可以对应多个槽,所有这里是void* _a[]数组



Q_OBJECT

从这里看是不是很明显的能看出,这里写了虚函数,然后生成moc文件帮我们实现

* qmake ignore 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 *, Q     QT_WARNING_POP \
    struct QPrivateSignal {}; \
    QT_ANNOTATE_CLASS(qt_qobject, "")


分析connect函数


检查函数

判断参数是否为NULL



给Sender赋值获取名字、对象、参数等



获取sender的Index,并且判断是否有效



接收方信息



判断信号槽是否被关联



判断接收方是否有效可以被关联



检查发送者和接收者参数是否一致



执行关联操作函数



进入实现函数

先是强转了sender 和receiver保存在两变量里面



检测版本



估计是为了防止多线程,给了锁



生成信号槽的对应关系Connection,并把Connection插入ConnectionList中



为对应的connect赋值



解锁,并且检查是否关联成功



分析activate函数


当我们发Sig1信号时调用的函数



红框计算父类的信号


bb3dbeed06a624b15ad279d1d126449d_640_wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1.jpg


前面做了一系列检查,是否还关联着



构造一个ConnectListRef的结构体,类似ConnectList引用



这里用上面的Index找到关联链表



遍历表找到对应得connect



调用链表得槽函数




大体图




相关文章
|
3月前
Qt类结构分析
Qt类结构分析
64 3
|
6月前
Qt第二课 核心机制信号槽
Qt第二课 核心机制信号槽
69 1
|
6月前
|
存储 API C++
【Qt 信号槽】深入探索 Qt 信号和槽机制中的引用传递“ (“A Deep Dive into Reference Passing in Qt Signal and Slot Mechanism“)
【Qt 信号槽】深入探索 Qt 信号和槽机制中的引用传递“ (“A Deep Dive into Reference Passing in Qt Signal and Slot Mechanism“)
558 0
|
6月前
【qt】核心机制信号槽(下)
【qt】核心机制信号槽(下)
41 1
|
6月前
|
消息中间件 存储 安全
深入理解 Qt 信号槽:高效沟通的桥梁
深入理解 Qt 信号槽:高效沟通的桥梁
779 1
|
3月前
从源码角度分析Qt元对象系统2
从源码角度分析Qt元对象系统
55 0
|
3月前
|
存储
从源码角度分析Qt元对象系统1
从源码角度分析Qt元对象系统
79 0
|
3月前
QT信号槽
QT信号槽
37 0
|
5月前
|
安全 BI 数据库
数据库大作业——基于qt开发的图书管理系统 (一)环境的配置与项目需求的分析
数据库大作业——基于qt开发的图书管理系统 (一)环境的配置与项目需求的分析
106 0