Qt 的信號與槽機制(Signal and Slot)是 Qt 框架 中用于對象間通信的核心機制之一。它允許對象之間進行松耦合的事件驅動式通信,尤其適合 GUI 應用程序 中的事件處理。
1. 基本概念
信號 (Signal)
- 當對象的狀態發生變化時,它會發出一個信號。
- 信號是一種聲明,不需要實現。
- 信號不需要知道誰會接收它,也就是松耦合的設計。
槽 (Slot)
- 槽是一個普通的函數,可以用來接收信號。
- 當信號發出時,與之關聯的槽函數將被自動調用。
- 槽可以是成員函數、普通函數或 lambda 表達式。
連接 (Connect)
- 信號和槽通過
QObject::connect()
進行連接。 - 當信號發出時,Qt 會自動調用與其連接的槽。
2. 信號與槽的工作原理
Qt 的信號與槽機制基于 元對象系統(Meta-Object System),它使用以下特性:
Q_OBJECT
宏:類中必須包含此宏,表示該類支持信號與槽。moc
工具:Qt 的元對象編譯器,會處理信號與槽相關的代碼。
Qt 的 事件循環 負責在對象間傳遞信號,確保槽在正確的上下文中執行。
3. 信號與槽的定義與使用
1) 定義一個信號與槽
要使用信號與槽,必須滿足以下條件:
- 類需要繼承自
QObject
。 - 在類聲明中加入
Q_OBJECT
宏。 - 使用
signals
關鍵字聲明信號。 - 使用
slots
關鍵字聲明槽函數。
示例代碼:
定義類:
#include <QObject>
#include <QDebug>class MyObject : public QObject {Q_OBJECTpublic:explicit MyObject(QObject *parent = nullptr) : QObject(parent) {}signals:void mySignal(int value); // 聲明一個信號public slots:void mySlot(int value) { // 定義一個槽qDebug() << "Received signal with value:" << value;}
};
連接信號與槽:
#include <QCoreApplication>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);MyObject obj1, obj2;// 連接信號與槽QObject::connect(&obj1, &MyObject::mySignal, &obj2, &MyObject::mySlot);// 觸發信號emit obj1.mySignal(42);return a.exec();
}
輸出結果:
Received signal with value: 42
2) 連接方式
QObject::connect
有多種形式,支持不同的連接模式。
語法:
QObject::connect(sender, SIGNAL(signalName(params)), receiver, SLOT(slotName(params)));
現代寫法(推薦): 使用函數指針和 lambda 表達式。
QObject::connect(sender, &ClassName::signalName, receiver, &ClassName::slotName);
Lambda 表達式示例:
QObject::connect(&obj1, &MyObject::mySignal, [](int value) {qDebug() << "Lambda slot received value:" << value;
});
3) 信號與槽的重載
如果信號或槽有多個重載版本,必須在連接時指定函數指針。
示例:
class Example : public QObject {Q_OBJECT
signals:void valueChanged(int);void valueChanged(double);
};Example obj;
QObject::connect(&obj, static_cast<void (Example::*)(int)>(&Example::valueChanged), &obj, [](int value) { qDebug() << "Int version called:" << value; });
4. 信號與槽的特點
- 松耦合:
- 信號的發送者和槽的接收者無需了解彼此的存在。
- 類型安全:
- 信號與槽的參數必須匹配,否則編譯器會報錯。
- 多對多關系:
- 一個信號可以連接多個槽。
- 多個信號可以連接到同一個槽。
- 線程安全:
- 跨線程連接時,信號會安全地傳遞到接收線程。
5. 信號與槽的連接類型
在多線程程序中,Qt 提供了不同的連接類型:
連接類型:
-
Qt::AutoConnection(默認)
- 如果信號和槽在同一線程中,則為直接連接。
- 如果在不同線程中,則為隊列連接。
-
Qt::DirectConnection
- 信號發出后,槽會立即執行,位于發送線程。
-
Qt::QueuedConnection
- 信號會進入接收線程的事件隊列,由接收線程執行。
-
Qt::BlockingQueuedConnection
- 發送線程阻塞,直到槽函數執行完畢。僅適用于跨線程。
6. 注意事項
- 使用
Q_OBJECT
宏:- 類必須繼承自
QObject
,并包含Q_OBJECT
宏,否則信號與槽不會工作。
- 類必須繼承自
-
moc 工具的支持:
- 需要使用 Qt 的 Meta-Object Compiler (moc) 進行預處理,否則信號與槽無法解析。
-
參數匹配:
- 信號與槽的參數個數和類型必須兼容。信號的參數可以多于槽的參數,但前者必須兼容后者。
-
析構時自動斷開連接:
- 當對象被銷毀時,Qt 會自動斷開與該對象相關的所有連接。
總結
Qt 的信號與槽機制是一種強大且靈活的事件處理機制,它提供了類型安全、松耦合的通信方式,廣泛用于 Qt 應用程序中的 組件間交互 和 事件處理。
現代 C++ 寫法(函數指針和 Lambda 表達式)更加簡潔和安全,推薦在新代碼中使用。