默認安裝好了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)
這個工具可以很好的分析源碼
案例代碼
#include <QObject>class QtClass : public QObject{ Q_OBJECTpublic: QtClass(QObject *parent); ~QtClass(); void Test(){ emit Sig1(5); }signals: void Sig1(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), QT_MOC_LITERAL(1, 8, 4), QT_MOC_LITERAL(2, 13, 0), QT_MOC_LITERAL(3, 14, 4), QT_MOC_LITERAL(4, 19, 5), QT_MOC_LITERAL(5, 25, 5) }, "QtClass\0Sig1\0\0nVal\0Slot1\0nVal0"};qt_meta_data_QtClass : 存儲類中函數相關的信息
static const uint qt_meta_data_QtClass[] = { 7, 0, 2, 14, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 24, 2, 0x06 , 4, 1, 27, 2, 0x08 ,24 QMetaType::Void, QMetaType::Int, 3,27 QMetaType::Void, QMetaType::Int, 5, 0 };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, "")
檢查函數
判斷參數是否為NULL
給Sender賦值獲取名字、對象、參數等
獲取sender的Index,並且判斷是否有效
接收方信息
判斷信號槽是否被關聯
判斷接收方是否有效可以被關聯
檢查發送者和接收者參數是否一致
執行關聯操作函數
進入實現函數
先是強轉了sender 和receiver保存在兩變量裡面
檢測版本
估計是為了防止多線程,給了鎖
生成信號槽的對應關係Connection,並把Connection插入ConnectionList中
為對應的connect賦值
解鎖,並且檢查是否關聯成功
當我們發Sig1信號時調用的函數
紅框計算父類的信號
前面做了一系列檢查,是否還關聯著
構造一個ConnectListRef的結構體,類似ConnectList引用
這裡用上面的Index找到關聯鍊表
遍歷表找到對應得connect
調用鍊表得槽函數