环境安装
默认安装好了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信号时调用的函数
红框计算父类的信号
前面做了一系列检查,是否还关联着
构造一个ConnectListRef的结构体,类似ConnectList引用
这里用上面的Index找到关联链表
遍历表找到对应得connect
调用链表得槽函数
大体图