事件驱动架构的核心思想是当某个特定事件发生时,系统能够自动通知所有关注该事件的对象并执行对应的处理逻辑,这种模式可以有效降低模块间的耦合度,让代码结构更清晰。在C++中实现事件驱动架构有多种成熟方案,开发者可以根据项目需求选择合适的实现方式。

基于观察者模式的实现方案
观察者模式是事件驱动架构最基础的实现思路,核心包含事件发布者和事件订阅者两个角色。发布者维护一个订阅者列表,当事件触发时遍历列表通知所有订阅者执行对应的处理逻辑。
基础实现示例
首先定义事件类型和订阅者接口:
#include <iostream>
#include <vector>
#include <string>
#include <functional>
// 定义事件类型枚举
enum class EventType {
EVENT_CLICK,
EVENT_KEY_PRESS,
EVENT_DATA_RECEIVE
};
// 事件数据结构
struct Event {
EventType type;
std::string data;
};
// 订阅者接口
class IEventListener {
public:
virtual ~IEventListener() = default;
virtual void onEvent(const Event& event) = 0;
};
接下来实现事件发布者:
// 事件发布者
class EventDispatcher {
private:
std::vector<IEventListener*> listeners;
public:
// 注册订阅者
void addListener(IEventListener* listener) {
listeners.push_back(listener);
}
// 移除订阅者
void removeListener(IEventListener* listener) {
for (auto it = listeners.begin(); it != listeners.end(); ++it) {
if (*it == listener) {
listeners.erase(it);
break;
}
}
}
// 触发事件,通知所有订阅者
void dispatchEvent(const Event& event) {
for (auto listener : listeners) {
listener->onEvent(event);
}
}
};
最后实现具体的订阅者并使用:
// 具体订阅者实现
class ClickListener : public IEventListener {
public:
void onEvent(const Event& event) override {
if (event.type == EventType::EVENT_CLICK) {
std::cout << "收到点击事件,数据:" << event.data << std::endl;
}
}
};
class DataReceiveListener : public IEventListener {
public:
void onEvent(const Event& event) override {
if (event.type == EventType::EVENT_DATA_RECEIVE) {
std::cout << "收到数据接收事件,数据:" << event.data << std::endl;
}
}
};
int main() {
EventDispatcher dispatcher;
ClickListener clickListener;
DataReceiveListener dataListener;
// 注册订阅者
dispatcher.addListener(&clickListener);
dispatcher.addListener(&dataListener);
// 触发事件
Event clickEvent{EventType::EVENT_CLICK, "按钮被点击"};
dispatcher.dispatchEvent(clickEvent);
Event dataEvent{EventType::EVENT_DATA_RECEIVE, "接收到100字节数据"};
dispatcher.dispatchEvent(dataEvent);
// 移除订阅者
dispatcher.removeListener(&clickListener);
dispatcher.dispatchEvent(clickEvent);
return 0;
}
基于回调函数的轻量方案
如果项目规模较小,不需要复杂的接口定义,可以使用回调函数的方式实现事件驱动,这种方式更轻量,代码更简洁。
实现示例
#include <iostream>
#include <vector>
#include <functional>
#include <string>
// 定义事件回调函数类型
using EventCallback = std::function<void(const std::string&)>;
// 事件管理器
class CallbackEventManager {
private:
std::vector<EventCallback> callbacks;
public:
// 注册回调
void registerCallback(EventCallback cb) {
callbacks.push_back(cb);
}
// 触发事件
void triggerEvent(const std::string& eventData) {
for (auto& cb : callbacks) {
cb(eventData);
}
}
};
// 普通函数作为回调
void onEvent1(const std::string& data) {
std::cout << "回调1处理事件:" << data << std::endl;
}
int main() {
CallbackEventManager manager;
// 注册普通函数回调
manager.registerCallback(onEvent1);
// 注册lambda表达式回调
manager.registerCallback([](const std::string& data) {
std::cout << "回调2处理事件:" << data << std::endl;
});
// 触发事件
manager.triggerEvent("测试事件数据");
return 0;
}
信号槽机制实现
信号槽是更灵活的事件驱动实现方式,支持一对多、多对一的连接关系,很多C++框架如Qt都内置了信号槽机制,我们也可以自己实现一个简单的信号槽系统。
简单信号槽实现
#include <iostream>
#include <vector>
#include <functional>
#include <memory>
// 信号类,支持无参数事件
class Signal {
private:
std::vector<std::function<void()>> slots;
public:
// 连接槽函数
void connect(std::function<void()> slot) {
slots.push_back(slot);
}
// 触发信号
void emit() {
for (auto& slot : slots) {
slot();
}
}
};
// 带参数的信号类模板
template <typename... Args>
class SignalWithArgs {
private:
std::vector<std::function<void(Args...)>> slots;
public:
void connect(std::function<void(Args...)> slot) {
slots.push_back(slot);
}
void emit(Args... args) {
for (auto& slot : slots) {
slot(args...);
}
}
};
// 测试类
class Sender {
public:
Signal clickSignal;
SignalWithArgs<int, std::string> dataSignal;
};
class Receiver {
public:
void onSendClick() {
std::cout << "收到点击信号" << std::endl;
}
void onSendData(int code, const std::string& msg) {
std::cout << "收到数据信号,code:" << code << ",msg:" << msg << std::endl;
}
};
int main() {
Sender sender;
Receiver receiver;
// 连接信号和槽
sender.clickSignal.connect(std::bind(&Receiver::onSendClick, &receiver));
sender.dataSignal.connect(std::bind(&Receiver::onSendData, &receiver, std::placeholders::_1, std::placeholders::_2));
// 触发信号
sender.clickSignal.emit();
sender.dataSignal.emit(200, "操作成功");
return 0;
}
不同方案的选择建议
三种实现方案各有适用场景:
- 观察者模式适合大型项目,需要明确的接口定义和事件类型管理,扩展性强
- 回调函数方案适合小型项目或者简单场景,实现简单,没有额外的接口负担
- 信号槽机制适合需要灵活连接关系的场景,支持多种参数传递,代码可读性更好
在实际开发中,还可以结合智能指针管理订阅者生命周期,避免悬空指针问题,同时可以添加事件优先级机制,让不同订阅者按照优先级顺序处理事件,满足更复杂的业务需求。