0%

信号与槽AI

1. 观察者模式 (Observer Pattern)

这是最直接、最经典的解决方案,也是Qt信号/槽机制的理论基础。

核心思想: 定义一种一对多的依赖关系,当一个对象(Subject,主题)的状态发生改变时,所有依赖于它的对象(Observers,观察者)都会得到通知并自动更新。

实现方式:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <iostream>
#include <vector>
#include <algorithm>
#include <memory>

// 前向声明
class Observer;

// 主题 (Subject) 接口
class Subject {
public:
virtual ~Subject() = default;
virtual void attach(std::weak_ptr<Observer> observer) = 0;
virtual void detach(std::weak_ptr<Observer> observer) = 0;
virtual void notify() = 0;
};

// 观察者 (Observer) 接口
class Observer {
public:
virtual ~Observer() = default;
virtual void update() = 0;
};

// 具体的主题(例如:后台工作线程)
class ConcreteSubject : public Subject, public std::enable_shared_from_this<ConcreteSubject> {
public:
void doWork() {
std::cout << "Working..." << std::endl;
// ... 一些工作 ...
std::cout << "Work done! Notifying observers." << std::endl;
notify(); // 工作完成,通知所有观察者
}

void attach(std::weak_ptr<Observer> observer) override {
observers_.push_back(observer);
}

void detach(std::weak_ptr<Observer> observer) override {
// 在实际项目中,这里需要更复杂的逻辑来清理失效的 weak_ptr
observers_.erase(
std::remove_if(observers_.begin(), observers_.end(),
[&observer](const std::weak_ptr<Observer>& wp) {
return wp.lock() == observer.lock();
}),
observers_.end());
}

void notify() override {
for (auto it = observers_.begin(); it != observers_.end(); ) {
if (auto obs = it->lock()) {
obs->update();
++it;
} else {
// 观察者对象已被销毁,移除失效的 weak_ptr
it = observers_.erase(it);
}
}
}

private:
std::vector<std::weak_ptr<Observer>> observers_;
};

// 具体的观察者(例如:UI更新器)
class ConcreteObserver : public Observer, public std::enable_shared_from_this<ConcreteObserver> {
public:
ConcreteObserver(const std::string& name) : name_(name) {}

void update() override {
std::cout << "Observer " << name_ << ": Received update! Updating UI..." << std::endl;
}

// 一个便捷函数,用于将自己注册到主题
void subscribeTo(const std::shared_ptr<Subject>& subject) {
subject_ = subject;
subject_->attach(std::weak_ptr<Observer>(shared_from_this()));
}

void unsubscribe() {
if (auto sub = subject_.lock()) {
sub->detach(std::weak_ptr<Observer>(shared_from_this()));
}
}

private:
std::string name_;
std::weak_ptr<Subject> subject_;
};

// 使用示例
int main() {
auto worker = std::make_shared<ConcreteSubject>();
auto uiUpdater1 = std::make_shared<ConcreteObserver>("UI 1");
auto uiUpdater2 = std::make_shared<ConcreteObserver>("UI 2");

// 建立订阅关系(松耦合:Worker不知道谁订阅了它)
uiUpdater1->subscribeTo(worker);
uiUpdater2->subscribeTo(worker);

// 执行工作,自动通知所有订阅者
worker->doWork();

// 一个观察者取消订阅
uiUpdater1->unsubscribe();
std::cout << "\n--- After unsubscribe ---\n";
worker->doWork();

return 0;
}

优点: 实现了松耦合,Subject不需要知道Observer的具体细节。
缺点: 需要自己管理Observer的注册、注销和生命周期(使用std::weak_ptr防止悬挂指针)。


2. 回调函数 (Callbacks) / 函数对象 (Function Objects / std::function)

这是一种更轻量级的方式,特别适合一对一的通信场景。

实现方式:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <functional>
#include <vector>

class Worker {
public:
// 使用 std::function 定义回调类型
using Callback = std::function<void(int result)>;

void setCompletionCallback(const Callback& cb) {
completionCallback_ = cb;
}

void addProgressCallback(const Callback& cb) {
progressCallbacks_.push_back(cb);
}

void doWork() {
for (int i = 0; i <= 100; i += 25) {
std::cout << "Progress: " << i << "%" << std::endl;
// 通知所有进度回调(模拟一对多)
for (const auto& cb : progressCallbacks_) {
if (cb) cb(i);
}
}
// 工作完成,通知完成回调
if (completionCallback_) {
completionCallback_(42); // 假设结果是42
}
}

private:
Callback completionCallback_;
std::vector<Callback> progressCallbacks_;
};

// 一个接收通知的类
class UI {
public:
void updateProgress(int value) {
std::cout << "UI: Updating progress bar to " << value << "%" << std::endl;
}

void handleResult(int result) {
std::cout << "UI: Got result: " << result << std::endl;
}
};

int main() {
Worker worker;
UI ui;

// 设置回调(建立连接)
worker.setCompletionCallback([&ui](int result) { ui.handleResult(result); });
worker.addProgressCallback([&ui](int progress) { ui.updateProgress(progress); });

// 也可以使用lambda直接处理
worker.addProgressCallback([](int progress) {
std::cout << "Logger: Progress is " << progress << "%" << std::endl;
});

worker.doWork();

return 0;
}

优点: 非常灵活,语法简单,C++11及以上标准支持良好。
缺点: 一对多需要自己管理回调列表。要非常小心回调函数和对象的生命周期(上面的例子使用了lambda捕获引用&ui,如果ui先于worker被销毁,就会导致未定义行为)。


3. 基于事件的系统 (Event Bus)

这是一个更高级、更通用的模式,常用于大型应用程序,它本身是一个中央调度器。

简化实现:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <map>
#include <vector>
#include <functional>
#include <memory>
#include <string>

// 任何需要传递的事件数据都可以继承自此类
struct Event {
virtual ~Event() = default;
};

// 具体的事件类型
struct WorkCompletedEvent : public Event {
int result;
WorkCompletedEvent(int r) : result(r) {}
};

struct ProgressUpdatedEvent : public Event {
int progress;
ProgressUpdatedEvent(int p) : progress(p) {}
};

// 事件总线(通常是单例)
class EventBus {
public:
using Subscriber = std::function<void(const Event&)>;

// 订阅特定类型的事件
template <typename EventType>
void subscribe(Subscriber subscriber) {
subscribers_[typeid(EventType).name()].push_back(subscriber);
}

// 发布一个事件
template <typename EventType>
void publish(const EventType& event) {
auto it = subscribers_.find(typeid(EventType).name());
if (it != subscribers_.end()) {
for (const auto& subscriber : it->second) {
subscriber(event);
}
}
}

private:
std::map<std::string, std::vector<Subscriber>> subscribers_;
};

// 使用示例
int main() {
EventBus bus;

Worker worker(bus); // 假设Worker接收一个EventBus引用
UI ui(bus); // UI也接收一个EventBus引用

// 在UI内部某处进行订阅
bus.subscribe<ProgressUpdatedEvent>([](const Event& e) {
const auto& event = static_cast<const ProgressUpdatedEvent&>(e);
std::cout << "UI Event: Progress " << event.progress << "%" << std::endl;
});

bus.subscribe<WorkCompletedEvent>([](const Event& e) {
const auto& event = static_cast<const WorkCompletedEvent&>(e);
std::cout << "UI Event: Result " << event.result << std::endl;
});

// Worker在执行过程中发布事件
// worker.doWork() 内部会调用 bus.publish(ProgressUpdatedEvent(50));
// 和 bus.publish(WorkCompletedEvent(100));

return 0;
}
// 注:这是一个简化版,完整实现需要考虑线程安全、异常处理等。

优点: 极度松耦合,组件之间通过事件通信,完全不知道对方的存在。非常灵活和强大。
缺点: 实现相对复杂,调试可能更困难(事件流不直观)。

总结:纯C++的解决方案

方法 适用场景 优点 缺点
观察者模式 经典的一对多通知,结构清晰 松耦合,意图明确 需要自己实现和管理,代码量稍大
回调函数 (std::function) 轻量级,一对一或简单一对多 灵活,简单,现代C++标准支持 生命周期管理需谨慎,一对多需自行维护列表
事件总线 (Event Bus) 大型应用,复杂组件间通信 极度松耦合,高度灵活 实现复杂,系统行为不易跟踪,调试难

Qt的信号/槽机制可以看作是这些模式的一个集大成者:
它提供了观察者模式的清晰结构,用起来像回调函数一样简单直观,并且通过元对象系统实现了比手动事件总线更强大、更安全、更易用的功能。