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_OBJECT
public:
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 表达式)更加简洁和安全,推荐在新代码中使用。