信号槽
信号槽
信号槽
“信号槽”(Signal and Slot)是一种常用于 事件驱动编程 的通信机制,最典型的代表是 Qt 框架中的实现方式。
它的核心思想是:一个对象(信号发送者)发出信号(Signal),另一个对象(槽函数)收到这个信号并作出响应。本质上是 观察者模式 的一种实现形式。
基本概念
名称 | 说明 |
---|---|
信号(Signal) | 某个事件发生时发送的通知 |
槽(Slot) | 对应信号的响应函数 |
连接(connect) | 将信号和槽绑定起来 |
信号槽工作流程(以 Qt 为例)
1
connect(sender, SIGNAL(signalName(args)), receiver, SLOT(slotFunction(args)));
当 sender
对象发出 signalName
信号时,receiver
的 slotFunction
就会被自动调用。
Qt 简单示例
1
2
3
4
5
// 假设有一个按钮和一个响应槽
QPushButton *button = new QPushButton("Click Me");
QObject::connect(button, &QPushButton::clicked, []() {
qDebug() << "Button clicked!";
});
当按钮被点击时,Qt 内部会自动调用你连接的槽函数(这里是 lambda)。
为什么需要信号槽?
- 松耦合:发送信号者和接收者之间不需要直接引用对方。
- 安全性高:信号槽机制是类型安全的(参数类型匹配检查)。
- 支持一对多、多对一通信。
- 线程安全(Qt 的 signal/slot 跨线程时自动使用事件队列)。
信号槽 vs 回调函数
特性 | 信号槽 | 回调函数 |
---|---|---|
类型安全 | ✅ 是 | ❌ 手动保证 |
多个接收者 | ✅ 支持多个槽 | ❌ 通常只能一个 |
解耦性 | ✅ 高 | ❌ 较低 |
易用性 | ✅ 清晰、现代 | ❌ 需要管理函数指针或绑定 |
跨线程通信(Qt 特有)
1
connect(sender, &Sender::signal, receiver, &Receiver::slot, Qt::QueuedConnection);
当信号和槽位于不同线程时,Qt 使用事件队列自动进行线程同步。
这极大简化了线程间通信的复杂度。
C++ 中自己实现信号槽机制
如果不使用 Qt,也可以用一些轻量级库来实现信号槽,例如:
- Boost.Signals2
- Nano-signal-slot(纯头文件)
- 自己手写(用
std::function
+std::vector
存回调)
简化版 C++ 实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream> // 引入标准输入输出库,用于 std::cout
#include <functional> // 引入 std::function,用于函数包装
#include <vector> // 引入 std::vector,用于存储多个槽函数
// Signal 类:实现简易的信号槽机制
class Signal {
public:
// 连接一个槽函数(slot),传入一个无参、无返回值的函数对象
void connect(std::function<void()> slot) {
slots.push_back(slot); // 将槽函数加入 slots 容器
}
// 触发信号(emit),调用所有已连接的槽函数
void emit() {
for (auto& slot : slots) // 遍历所有槽
slot(); // 依次调用
}
private:
std::vector<std::function<void()>> slots; // 用于保存所有连接的槽函数
};
int main() {
Signal sig; // 创建一个信号对象
// 使用 Lambda 表达式连接一个槽函数
sig.connect([]() {
std::cout << "Hello\n"; // 当信号触发时输出 "Hello"
});
// 发出信号,自动调用所有连接的槽函数
sig.emit(); // 输出:Hello
}
本文由作者按照 CC BY 4.0 进行授权