Qt / Moc 和信号 - 槽解析
目錄
一. MOC
二. moc_test.cpp 分析
三. connect
四. activate
五. 總結
版本 Qt5.12.3 moc_test.cpp 位于可執行文件目錄下,其余源代碼都位于 Qt5.12.3\5.12.3\Src\qtbase\src\corelib\kernel 中。
一. MOC
1.1 簡介
元對象編譯器 moc(Meta-Object Compiler)是 Qt 對 C++ 的擴展。可以先把它看作一個代碼生成器。以 test 類為例,構建項目時,moc 讀取 C++ 頭文件。如果在 test.h 中找到 Q_OBJECT 宏,它將生成一個 moc_test.cpp,其中包含 test 類的元對象(metaObject)代碼。這個新的文件和 test.cpp 一起參與編譯,最終被鏈接到二進制代碼中去。
Qt 將源代碼交給標準 C++編譯器,如 gcc 之前,需要事先將擴展的語法(Q_OBJECT、SLOT、SIGNAL等)展開來。完成這一操作的就是 moc。
可以在命令行下輸入 moc test.h -o moc_test.cpp手動生成。
1.2 宏
Q_OBJECT、SLOT、SIGNAL、emit、Q_INVOKABLE 等宏是 Qt 擴展的語法,它們其實定義在 qobjectdefs.h 中,編譯時被 moc 展開。
二. moc_test.cpp 分析
先貼一下代碼:
test.h
class test : public QObject {Q_OBJECT public:explicit test(QObject *parent = nullptr);int getValue(){return m_value;}Q_INVOKABLE void identifyByMoc();signals:void testSignal1();void testSignal2();void testSignal3();void valueChanged(int newValue);public slots:void testSlot1();void testSlot2();void setValue(int value);private:int m_value;};test.cpp
#include "test.h"test::test(QObject *parent) : QObject(parent) {m_value = 0; }void test::identifyByMoc(){qDebug()<<"Identified By Moc"; }void test::testSlot1(){qDebug()<<"Invoke testSlot1"; }void test::testSlot2(){qDebug()<<"Invoke testSlot2"; }void test::setValue(int value) {m_value = value;emit valueChanged(value); }main.cpp
#include “test.h”void test_value() {test a,b;QObject::connect(&a,SIGNAL(valueChanged(int)),&b,SLOT(setValue(int)));QObject::connect(&a,SIGNAL(valueChanged(int)),&a,SLOT(testSlot1()));qDebug()<<SIGNAL(valueChanged(int))<<SLOT(setValue(int));qDebug()<<"before: b.getValue ="<<b.getValue();a.setValue(12);qDebug()<<"after: b.getValue ="<<b.getValue(); }?moc_test.cpp
/**************************************************************************** ** Meta object code from reading C++ file 'test.h' ** ** Created by: The Qt Meta Object Compiler version 67 (Qt 5.12.3) ** ** WARNING! All changes made in this file will be lost! *****************************************************************************/ #include "../../mocTest/test.h" #include <QtCore/qbytearray.h> #include <QtCore/qmetatype.h> #if !defined(Q_MOC_OUTPUT_REVISION) #error "The header file 'test.h' doesn't include <QObject>." #elif Q_MOC_OUTPUT_REVISION != 67 #error "This file was generated using the moc from 5.12.3. It" #error "cannot be used with the include files from this version of Qt." #error "(The moc has changed too much.)" #endifQT_BEGIN_MOC_NAMESPACE QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED//此處省略見下文QT_WARNING_POP QT_END_MOC_NAMESPACEmoc_test.cpp 看起來很復雜,分三部分來看:
2.1、類信息
位于 moc_test.cpp 頭部
//定義保存類信息的結構體 struct qt_meta_stringdata_test_t {QByteArrayData data[12];//保存類中被 QT 宏修飾過的函數名稱。char stringdata0[113]; };?QT_MOC_LITERAL 宏的作用是為 stringdata0 中保存的每個函數名都創建一個 QByteArrayData,宏參數為函數的索引值,偏移量,函數名長度。
/宏參數為函數的索引值,偏移量,函數名長度。 #define QT_MOC_LITERAL(idx, ofs, len) \Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \qptrdiff(offsetof(qt_meta_stringdata_test_t, stringdata0) + ofs - idx * sizeof(QByteArrayData)))//初始化保存類信息的結構體 static const qt_meta_stringdata_test_t qt_meta_stringdata_test = {{QT_MOC_LITERAL(0, 0, 4), // "test"QT_MOC_LITERAL(1, 5, 11), // "testSignal1"QT_MOC_LITERAL(2, 17, 0), // ""QT_MOC_LITERAL(3, 18, 11), // "testSignal2"QT_MOC_LITERAL(4, 30, 11), // "testSignal3"QT_MOC_LITERAL(5, 42, 12), // "valueChanged"QT_MOC_LITERAL(6, 55, 8), // "newValue"QT_MOC_LITERAL(7, 64, 9), // "testSlot1"QT_MOC_LITERAL(8, 74, 9), // "testSlot2"QT_MOC_LITERAL(9, 84, 8), // "setValue"QT_MOC_LITERAL(10, 93, 5), // "value"QT_MOC_LITERAL(11, 99, 13) // "identifyByMoc"},"test\0testSignal1\0\0testSignal2\0testSignal3\0""valueChanged\0newValue\0testSlot1\0""testSlot2\0setValue\0value\0identifyByMoc"}; #undef QT_MOC_LITERALstatic const uint qt_meta_data_test[] = {//保存了信號,槽及 moc 識別的其他函數的個數,函數索引,返回類型,參數個數,參數類型等信息。// content:8, // revision0, // classname0, 0, // classinfo8, 14, // methods0, 0, // properties0, 0, // enums/sets0, 0, // constructors0, // flags4, // signalCount// signals: name, argc, parameters, tag, flags1, 0, 54, 2, 0x06 /* Public */,3, 0, 55, 2, 0x06 /* Public */,4, 0, 56, 2, 0x06 /* Public */,5, 1, 57, 2, 0x06 /* Public */,// slots: name, argc, parameters, tag, flags7, 0, 60, 2, 0x0a /* Public */,8, 0, 61, 2, 0x0a /* Public */,9, 1, 62, 2, 0x0a /* Public */,// methods: name, argc, parameters, tag, flags11, 0, 65, 2, 0x02 /* Public */,// signals: parametersQMetaType::Void,QMetaType::Void,QMetaType::Void,QMetaType::Void, QMetaType::Int, 6,// slots: parametersQMetaType::Void,QMetaType::Void,QMetaType::Void, QMetaType::Int, 10,// methods: parametersQMetaType::Void,0 // eod };2.2、signals 實現
位于 moc_test.cpp 底部:
// SIGNAL 0 void test::testSignal1() {QMetaObject::activate(this, &staticMetaObject, 0, nullptr); }// SIGNAL 1 void test::testSignal2() {QMetaObject::activate(this, &staticMetaObject, 1, nullptr); }// SIGNAL 2 void test::testSignal3() {QMetaObject::activate(this, &staticMetaObject, 2, nullptr); }// SIGNAL 3 void test::valueChanged(int _t1) {void *_a[] = {nullptr, const_cast<void *>(reinterpret_cast<const void *>(std::addressof(_t1)))};QMetaObject::activate(this, &staticMetaObject, 3, _a); }test.h 中構造函數一個,普通函數一個(getValue),signal 4 個,slot 3 個,Q_INVOKABLE 修飾的方法一個。其中 3 個槽函數需要自己在 test.cpp 里實現,4個信號函數由 moc 自動在 moc_test.cpp 中實現,在注釋可以看到索引根據聲明順序分別為 0,1,2,3。
2.3、Q_OBJECT 宏的展開
查找 qobjectdefs.h 可以找到 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 *,QMetaObject::Call, int, void **); \QT_WARNING_POP \struct QPrivateSignal {}; \ QT_ANNOTATE_CLASS(qt_qobject, "")正對應 moc_test.cpp 里的函數等,我們取重要的幾個:
位于moc_test.cpp中部:
2.3.1、qt_static_metacall
void test::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) {if (_c == QMetaObject::InvokeMetaMethod){auto *_t = static_cast<test *>(_o);Q_UNUSED(_t)switch (_id){case 0:_t->testSignal1();break;case 1:_t->testSignal2();break;case 2:_t->testSignal3();break;case 3:_t->valueChanged((*reinterpret_cast<int(*)>(_a[1])));break;case 4:_t->testSlot1();break;case 5:_t->testSlot2();break;case 6:_t->setValue((*reinterpret_cast<int(*)>(_a[1])));break;case 7:_t->identifyByMoc();break;default:;}}else if (_c == QMetaObject::IndexOfMethod){int *result = reinterpret_cast<int *>(_a[0]);{using _t = void (test::*)();if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&test::testSignal1)){*result = 0;return;}}{using _t = void (test::*)();if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&test::testSignal2)){*result = 1;return;}}{using _t = void (test::*)();if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&test::testSignal3)){*result = 2;return;}}{using _t = void (test::*)(int);if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&test::valueChanged)){*result = 3;return;}}} }根據函數索引調用槽函數,在這里可以看出信號函數也可以當作槽函數一樣被調用,這也是信號槽調用過程的最后一步(先留個印象)。
2.3.2、staticMetaObject
test 類的元對象(QMetaObject),保存了 test 類的信息。保存的數據 qt_meta_stringdata_test.data 及 qt_meta_data_test 在 moc 文件的頂部定義并初始化。
QT_INIT_METAOBJECT const QMetaObject test::staticMetaObject = {{QMetaObject::SuperData::link<QObject::staticMetaObject>(),qt_meta_stringdata_test.data,qt_meta_data_test,qt_static_metacall,nullptr,nullptr}};2.3.3、metaObject
返回當前的 QMetaObject,一般是返回 staticMetaObject,即 2 介紹的。
const QMetaObject *test::metaObject() const {return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; }2.3.4、qt_metacast
類型轉換
void *test::qt_metacast(const char *_clname) {if (!_clname)return nullptr;if (!strcmp(_clname, qt_meta_stringdata_test.stringdata0))return static_cast<void *>(this);return QObject::qt_metacast(_clname); }2.3.5、qt_metacall
int test::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 < 8)qt_static_metacall(this, _c, _id, _a);_id -= 8;}else if (_c == QMetaObject::RegisterMethodArgumentMetaType){if (_id < 8)*reinterpret_cast<int *>(_a[0]) = -1;_id -= 8;}return _id; }在內部調用了 qt_static_metacall 。
總結一下,Moc 的作用:
把 Q_OBJECT、SIGNAL、Q_INVOKABLE 等宏展開。
保存類中特定函數(signals、slots 標簽下的函數及 Q_INVOKABLE 修飾的函數等)的信息。
創建槽函數的回調。
三. connect
要使用 Qt 的信號 - 槽機制,必須要 connect 這一步。我們查看 QObject::connect 的源碼:
QObject.cpp
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,constQObject *receiver, const char *method, Qt::ConnectionType type) { //此處省略了函數參數檢查,信號及槽函數索引獲取,connect類型處理等代碼。 //最后其實調用了QMetaObjectPrivate::connectQMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));return handle; }QMetaObjectPrivate::connect 同樣位于 QObject.cpp 中,
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) {QObject *s = const_cast<QObject *>(sender);QObject *r = const_cast<QObject *>(receiver);int method_offset = rmeta ? rmeta->methodOffset() : 0;Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);QObjectPrivate::StaticMetaCallFunction callFunction =rmeta ? rmeta->d.static_metacall : 0;QOrderedMutexLocker locker(signalSlotLock(sender),signalSlotLock(receiver));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;}// QObjectPrivate::Connection實例化, //存儲了信號-槽鏈接的信息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;//addConnection為信號發送者s保存了這個信號-槽鏈接,具體保存了什么,還需要//分析QObjectPrivate::Connection以及QObjectPrivate::addConnectionQObjectPrivate::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(); }qobject_p.h 中定義了 class QObjectPrivate
它的幾個成員如下:
struct Connection {QObject *sender;QObject *receiver;union {StaticMetaCallFunction callFunction;QtPrivate::QSlotObjectBase *slotObj;};// The next pointer for the singly-linked ConnectionListConnection *nextConnectionList;//senders linked listConnection *next;Connection **prev;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//省略部分代碼 };Connection 結構體保存了一個連接。其中的信息包括信號發送者指針,信號接收者指針以及指向下一個 Connection 的指針,信號索引,連接類型等。
一個信號可以對應多個槽函數,這里用 ConnectionList 保存一個信號對應的所有連接。它是一個單向鏈表,每個節點都是一個 Connection,通過它內部的 nextConnectionList 指針指向下一個Connection。在這里僅保存頭尾指針即可。
struct ConnectionList {ConnectionList() : first(nullptr), last(nullptr) {}Connection *first;Connection *last; };connectionLists 保存此對象作為信號發送者所對應的所有連接。這個向量里每個元素都是一個 ConnectionList 單鏈表。
QObjectConnectionListVector *connectionLists;QObject.cpp 里定義了 QObjectConnectionListVector 。
class QObjectConnectionListVector : public QVector<QObjectPrivate::ConnectionList> { public:bool orphaned; //the QObject owner of this vector has been destroyed while the vector was inUsebool dirty; //some Connection have been disconnected (their receiver is 0) but not removed from the list yetint inUse; //number of functions that are currently accessing this object or its connectionsQObjectPrivate::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 *next 和 Connection **prev 是此對象作為信號接收者時,保存發送者的雙向鏈表的操作指針,這里的內容待以后補充。
接下來看最后的 addConnection, 位于 QObject.cpp
void QObjectPrivate::addConnection(int signal, Connection *c) {Q_ASSERT(c->sender == q_ptr);if (!connectionLists)connectionLists = new QObjectConnectionListVector();if (signal >= connectionLists->count())connectionLists->resize(signal + 1);//根據信號索引取得此信號所對應的鏈表,并把此連接加入鏈表中。ConnectionList &connectionList = (*connectionLists)[signal];if (connectionList.last) {connectionList.last->nextConnectionList = c;}else {connectionList.first = c;}connectionList.last = c;cleanConnectionLists();//下面是對Connection* QObjectPrivate::senders雙向鏈表的操作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));} }四. activate
連接完成后,我們調用信號函數,從 moc_test.cpp 里對信號函數的實現可知,其實是調用了 QMetaObject::activate(this, &staticMetaObject, 3, _a);
// SIGNAL 3 void test::valueChanged(int _t1) {void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };QMetaObject::activate(this, &staticMetaObject, 3, _a); }QMetaObject::activate 源碼在 QObject.cpp 中
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index, void **argv) {activate(sender, QMetaObjectPrivate::signalOffset(m), local_signal_index, argv); }下面的 activate 函數省略了部分代碼,方便理解。
void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv) {int signal_index = signalOffset + local_signal_index;//判斷是否有與該信號相連接的接收對象if(!sender->d_func()->isSignalConnected(signal_index))return; // nothing connected to these signals, and no spy//給信號量加鎖,因為在connectionLists里所有的操作都是線程安全的QMutexLocker locker(signalSlotLock(sender));//獲取與該信號的ConnectionList鏈表QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;const QObjectPrivate::ConnectionList *list = &connectionLists->at(signal_index);QObjectPrivate::Connection *c = list->first;if (!c) continue;QObjectPrivate::Connection *last = list->last;//循環執行該信號對應的所有槽函數do {if (!c->receiver)continue;QObject * const receiver = c->receiver;const bool receiverInSameThread = QThread::currentThreadId() == receiver->d_func()->threadData->threadId;// 決定該連接是馬上響應還是把它放到事件隊列中if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)|| (c->connectionType == Qt::QueuedConnection)) {queued_activate(sender, signal_index, c, argv);continue;} else if (c->connectionType == Qt::BlockingQueuedConnection) {continue;}QConnectionSenderSwitcher sw;if (receiverInSameThread)sw.switchSender(receiver, sender, signal_index);const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;const int method_relative = c->method_relative;//下面if-else結構包含三種調用槽函數的方式。if (c->isSlotObject) {c->slotObj->ref();QScopedPointer<QtPrivate::QSlotObjectBase,QSlotObjectBaseDeleter>obj(c->slotObj);locker.unlock();Q_TRACE(QMetaObject_activate_begin_slot_functor, obj.data());//一,通過call調用接收者中的槽函數obj->call(receiver, argv ? argv : empty_argv);Q_TRACE(QMetaObject_activate_end_slot_functor, obj.data());obj.reset();locker.relock();} else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {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);Q_TRACE(QMetaObject_activate_begin_slot, receiver, methodIndex);//二,callFunction即moc_test.cpp里的qt_static_metacallcallFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);Q_TRACE(QMetaObject_activate_end_slot, receiver, methodIndex);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);}Q_TRACE(QMetaObject_activate_begin_slot, receiver, method);//三,metacall即moc_test.cpp里的qt_metacallmetacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);Q_TRACE(QMetaObject_activate_end_slot, receiver, method);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); }第二種方法解析:
qobjectdefs.h
struct Q_CORE_EXPORT QMetaObject { struct { // private dataconst QMetaObject *superdata;const QByteArrayData *stringdata;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; }moc_test.cpp 用 qt_static_metacall 實例化 d.static_metacall?
QT_INIT_METAOBJECT const QMetaObject test::staticMetaObject = { {&QObject::staticMetaObject,qt_meta_stringdata_test.data,qt_meta_data_test,qt_static_metacall,nullptr,nullptr } };qobject.cpp
QObjectPrivate::Connection *QMetaObjectPrivate::connect() {QObjectPrivate::StaticMetaCallFunction callFunction = rmeta ? rmeta->d.static_metacall : 0; }void QMetaObject::activate() {const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction; callFunction(...); //最后調用callFunction其實就是調用qt_static_metacall,還記的文章前邊說的留個印象么,現在就到了調用的最后一步。 }第三種方法解析:
qmetaobject.cpp
int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv) {if (object->d_ptr->metaObject)return object->d_ptr->metaObject->metaCall(object, cl, idx, argv);elsereturn object->qt_metacall(cl, idx, argv); }moc_test.cpp
int test::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 < 8)qt_static_metacall(this, _c, _id, _a);_id -= 8;}else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {if (_id < 8)*reinterpret_cast<int*>(_a[0]) = -1;_id -= 8;}return _id; }qobject.cpp
void QMetaObject::activate() {metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); }metacall 其實就是調用 qt_metacall,在內部最終還是調用了qt_static_metacall
五. 總結
5.1 解析
Moc 幫我們解析頭文件中 Qt 特有的宏,將函數信息保存在數組中,構建信號函數的實現,槽函數的回調。
5.2 保存
QObject::connect 將連接信息保存在結構體 Connection 中,一個信號可對應多個槽函數,這就形成了單鏈表 ConnectionList,然后以信號在 test 類的 MetaObject 中的索引作為向量的索引保存所有鏈表,形成 QObjectConnectionListVector* connectionLists 。
5.3 調用
調用信號函數時,其實是調用activate函數,最終回到qt_static_metacall,調用槽函數。
?
轉載于:https://blog.csdn.net/z_QaQ/article/details/105382418
?
(SAW:Game Over!)
總結
以上是生活随笔為你收集整理的Qt / Moc 和信号 - 槽解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OS / Linux / pthread
- 下一篇: Qt / QMainWindow、QDi