信号槽是一种用于对象间通信的观察者模式实现,广泛用于事件驱动编程。它实现了松耦合的事件处理系统。
核心概念
-
信号(Signal)
-
事件发射器,不关心谁接收
-
本质是函数调用接口
-
示例:
button.clicked()
-
-
槽(Slot)
-
事件处理器,不关心谁触发
-
可以是任何可调用对象(函数/lambda/成员函数)
-
示例:
void onButtonClick()
-
-
连接(Connection)
-
建立信号与槽的关联关系
-
通常返回连接对象用于管理生命周期
-
工作原理
// 伪代码示例
signal.connect(slot); // 建立连接
signal.emit(args); // 触发时调用所有连接的slot
关键特性
特性 | 说明 |
---|---|
松耦合 | 信号源和接收方互不知晓 |
多对多关系 | 一个信号可连接多个槽,一个槽可接收多个信号 |
线程安全 | 通常提供跨线程调用支持(如Qt的QueuedConnection) |
类型安全 | 编译期检查参数类型匹配 |
自动连接管理 | 对象销毁时自动断开连接 |
FastSignals
FastSignals 是一个 C++ 信号/槽(Signal/Slot)库,类似于 Boost.Signals2 或 Qt 的信号槽机制,但专注于 高性能 和 低开销。它适用于需要高效事件处理的场景,比如游戏引擎、高频交易系统或嵌入式开发。
核心特性
-
高性能信号触发
-
优化了信号发射(emit)和槽函数调用的速度,比传统回调(如函数指针或虚函数)更快。
-
适合对实时性要求高的场景。
-
-
线程安全支持
-
可配置为线程安全模式(需用户自行管理锁)。
-
-
灵活的连接方式
-
支持连接普通函数、类成员函数、lambda 表达式等。
-
可动态断开(disconnect)槽函数。
-
-
纯头文件库(Header-Only)
-
无需编译,直接包含头文件即可使用。
-
-
现代 C++ 支持(C++11/14/17+)
-
基于模板和移动语义实现,避免额外开销。
-
简单示例
#include <FastSignals/fast_signal.hpp>
#include <iostream>
// 定义一个按钮类,包含一个信号
struct Button {
FastSignal<void()> onClick; // 无参数的信号
};
// 槽函数
void onButtonClicked() {
std::cout << "按钮被点击了!" << std::endl;
}
int main() {
Button button;
button.onClick.connect(&onButtonClicked); // 连接信号和槽
button.onClick.emit(); // 触发信号(调用槽函数)
return 0;
}
libsigc++ 信号/槽库
libsigc++ 是 C++ 的一个高效、类型安全的信号与槽(Signal/Slot)库,广泛应用于 GTK(如 GTKmm)等开源项目中。它提供了一种灵活的方式来实现 观察者模式 和 事件驱动编程,比传统的回调函数更安全、更易用。
1. libsigc++ 核心概念
(1) 信号(Signal)
-
信号的本质:一个可以触发多个回调函数的对象。
-
信号的作用:当某个事件发生时,通知所有已连接的槽(Slots)。
-
示例:
sigc::signal<void(int, int)> signal_button_clicked;
(2) 槽(Slot)
-
槽的本质:一个可调用对象(函数、成员函数、Lambda、Functor 等)。
-
槽的作用:当信号触发时,执行相应的逻辑。
-
示例:
void on_button_clicked(int x, int y) { ... }
(3) 连接(Connection)
-
连接的作用:绑定信号和槽,使信号触发时能调用槽。
-
连接的管理:
-
可以手动断开连接(
disconnect()
)。 -
支持自动断开(当槽所属对象销毁时)。
-
-
示例:
auto conn = signal_button_clicked.connect(sigc::ptr_fun(on_button_clicked));
conn.disconnect(); // 手动断开
Nano Signals 11 信号库
Nano Signals 11 是一个超轻量级的 C++11 信号/槽(Signal/Slot)库,以其极简设计和高效性能著称。以下是该库的详细解析:
核心特性
-
极简头文件实现:仅需单个头文件(
nano_signal_slot.hpp
) -
零依赖:完全基于C++11标准,不依赖Boost/QT等
-
高性能:优化过的模板实现,调用开销极低
-
类型安全:编译期类型检查
-
线程不安全:设计简单不处理线程同步
基本用法
#include "nano_signal_slot.hpp"
// 定义信号
Nano::Signal<void(int, const std::string&)> signal;
// 连接普通函数
void free_func(int i, const std::string& s) { /*...*/ }
signal.connect<&free_func>();
// 连接成员函数
struct MyClass {
void method(int i, const std::string& s) { /*...*/ }
};
MyClass obj;
signal.connect<MyClass, &MyClass::method>(&obj);
// 触发信号
signal.emit(42, "hello");
实现原理
1 信号存储结构
template <typename... Args>
class Signal {
std::vector<std::function<void(Args...)>> slots;
};
2 连接机制
-
使用模板参数捕获可调用对象
-
存储为
std::function
对象 -
直接编译期绑定,无运行时类型检查开销
Nuclex 信号槽系统
Nuclex 是一个轻量级的 C++ 信号槽实现,专注于游戏开发和高性能场景。
核心特性
-
极简设计:
-
单头文件实现(通常为
Nuclex/Signals/Signal.h
) -
无外部依赖,纯 C++11/14 实现
-
-
高性能优化:
-
使用模板元编程减少运行时开销
-
内联函数调用优化
-
避免虚函数和动态内存分配
-
-
线程模型:
-
默认为非线程安全(高性能模式)
-
可选线程安全版本(带锁实现)
-
基本用法示例
#include <Nuclex/Signals/Signal.h>
// 定义信号类型
using ButtonClickSignal = Nuclex::Signals::Signal<void(int x, int y)>;
// 创建信号实例
ButtonClickSignal buttonClicked;
// 连接槽函数(lambda)
auto connection = buttonClicked.Connect([](int x, int y) {
std::cout << "Clicked at: " << x << ", " << y << std::endl;
});
// 发射信号
buttonClicked.Emit(100, 200);
// 断开连接
connection.Disconnect();
关键技术实现
连接存储结构
template <typename... TArgs>
class Signal {
private:
using SlotType = std::function<void(TArgs...)>;
std::vector<SlotType> slots;
};
线程安全版本
template <typename... TArgs>
class ThreadSafeSignal {
std::mutex mutex;
std::vector<SlotType> slots;
// 所有操作带锁
};
Qt信号槽机制
Qt信号槽机制的实现原理基于元对象系统(Meta-Object System),通过编译时的代码生成和运行时的动态连接,实现对象间松耦合的通信。 具体原理可分为以下关键点:
元对象系统与moc编译器
- 元对象系统是Qt框架的核心,为信号槽提供运行时类型信息(RTTI)和动态调用机制:
- 编译时,Qt的元对象编译器(moc)会解析带有
Q_OBJECT
宏的类,生成包含信号和槽信息的元对象代码(如moc_*.cpp
文件)。 - 生成的元对象代码包含:
- 信号和槽的名称、参数类型等信息。
- 动态查找和调用槽函数所需的映射表。
- 编译时,Qt的元对象编译器(moc)会解析带有
信号与槽的连接过程
- 通过
QObject::connect()
建立动态关联:- 发送对象(sender)发出信号时,Qt通过元对象系统查找其关联的槽函数列表。
- 信号与槽的参数类型必须严格匹配或兼容,确保安全调用(参数传递通过元对象系统完成)。
- 连接方式支持:
- 一对一:一个信号对应一个槽。
- 一对多:一个信号触发多个槽。
- 多对一:多个信号触发同一槽。
运行时的动态特性
- 信号触发时自动调用关联槽函数:
- 用
emit
发射信号,本质是调用moc生成的信号代理函数,遍历所有连接的槽并执行。 - 槽函数可设置为同步或异步执行(如跨线程通信时会自动排队)。
- 用
Qt信号槽的核心原理依赖于元对象系统在编译时生成代码信息,运行时动态解析和调用,实现了高度解耦的通信模式。
主流第三方库对比
特性 | Qt信号槽 | Boost.Signals2 | libsigc++ | NanoSignals | Nuclex |
---|---|---|---|---|---|
线程安全 | 是 | 是 | 可选 | 否 | 可选 |
依赖 | Qt框架 | Boost | 独立 | 无 | 无 |
性能 | 中等 | 中等 | 高 | 极高 | 极高 |
自动连接管理 | 是(QObject) | 是 | 是(trackable) | 否 | 分组管理 |
C++标准要求 | C++11 | C++11 | C++11 | C++11 | C++14 |
适用场景
✔ 需要高性能事件处理 的项目(如游戏、实时系统)。
✔ 不想引入大型库(如 Boost 或 Qt)的轻量级解决方案。
✔ 现代 C++(C++11 及以上)项目。✔ Qt应用程序:优先使用原生Qt信号槽
✔ 游戏开发:Nuclex或NanoSignals
✔ 高频交易系统:Boost.Signals2(线程安全)
✔ 嵌入式系统:NanoSignals(零开销)
✔ 跨平台库:libsigc++
链接
Boost.Signals2:Chapter 67. Boost.Signals2
Libsigc++:libsigc++ -- The Typesafe Callback Framework for C++: libsigc++
LSignal: GitHub - cpp11nullptr/lsignal: C++ signal and slot system
Nano Signals 11: GitHub - FrankHB/nano-signal-slot: Pure C++11 Signals and Slots
Nano Signals 17: GitHub - NoAvailableAlias/nano-signal-slot: Pure C++17 Signals and Slots
Nuclex: https://devel.nuclex.org/framework/browse/listing.php?repname=Framework&path=/Nuclex.Support.Native/trunk/Include/Nuclex/Support/Events/
Sigs: GitHub - netromdk/sigs: Simple thread-safe signal/slot C++17 include-only library.