1. 观察者模式的核心概念与经典实现
观察者模式是软件设计中最常用的行为型模式之一,它定义了对象间一对多的依赖关系,当一个对象状态发生改变时,所有依赖它的对象都会自动收到通知并更新。这种模式在GUI事件处理、消息队列、实时数据监控等场景中应用广泛。
在C++中,经典的观察者模式通常包含以下核心组件:
cpp复制// 抽象观察者接口
class Observer {
public:
virtual ~Observer() = default;
virtual void update(const std::string& message) = 0;
};
// 具体观察者实现
class ConcreteObserver : public Observer {
public:
void update(const std::string& message) override {
std::cout << "Received: " << message << std::endl;
}
};
// 抽象主题接口
class Subject {
public:
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify(const std::string& message) = 0;
};
// 具体主题实现
class ConcreteSubject : public Subject {
private:
std::vector<Observer*> observers_;
public:
void attach(Observer* observer) override {
observers_.push_back(observer);
}
void detach(Observer* observer) override {
observers_.erase(std::remove(observers_.begin(), observers_.end(), observer),
observers_.end());
}
void notify(const std::string& message) override {
for (auto observer : observers_) {
observer->update(message);
}
}
};
这种经典实现虽然简单直接,但在实际工程中会遇到几个典型问题:
- 内存管理风险:裸指针的使用可能导致悬挂指针或内存泄漏
- 线程安全问题:在多线程环境下,观察者的添加/删除和通知过程需要同步
- 性能瓶颈:当观察者数量庞大时,线性通知机制可能成为性能瓶颈
- 强耦合问题:观察者必须继承特定接口,限制了灵活性
2. 现代C++中的观察者模式变体
2.1 基于std::function的轻量级实现
C++11引入的std::function和lambda表达式为我们提供了更灵活的观察者模式实现方式:
cpp复制class EventSubject {
private:
std::vector<std::function<void(const std::string&)>> callbacks_;
public:
template <typename Observer>
void registerObserver(Observer&& observer) {
callbacks_.emplace_back(std::forward<Observer>(observer));
}
void notify(const std::string& message) {
for (auto& callback : callbacks_) {
callback(message);
}
}
};
// 使用示例
EventSubject subject;
subject.registerObserver([](const std::string& msg) {
std::cout << "Lambda observer: " << msg << std::endl;
});
struct FunctorObserver {
void operator()(const std::string& msg) const {
std::cout << "Functor observer: " << msg << std::endl;
}
};
subject.registerObserver(FunctorObserver{});
这种变体的优势在于:
- 不再需要继承特定接口,任何可调用对象都可以作为观察者
- 天然支持lambda表达式,代码更简洁
- 避免了虚函数调用的开销
2.2 线程安全的观察者模式实现
在多线程环境下,我们需要确保观察者注册/注销和通知操作的线程安全:
cpp复制#include <mutex>
#include <shared_mutex>
class ThreadSafeSubject {
private:
std::vector<std::function<void(const std::string&)>> callbacks_;
mutable std::shared_mutex mutex_;
public:
template <typename Observer>
void registerObserver(Observer&& observer) {
std::unique_lock lock(mutex_);
callbacks_.emplace_back(std::forward<Observer>(observer));
}
void unregisterObserver(size_t index) {
std::unique_lock lock(mutex_);
if (index < callbacks_.size()) {
callbacks_.erase(callbacks_.begin() + index);
}
}
void notify(const std::string& message) {
std::shared_lock lock(mutex_);
auto localCallbacks = callbacks_; // 创建本地副本
lock.unlock();
for (auto& callback : localCallbacks) {
callback(message);
}
}
};
关键设计考虑:
- 使用读写锁(std::shared_mutex)优化读多写少的场景
- 通知时创建观察者列表的本地副本,减少锁持有时间
- 提供观察者注销的索引机制,避免直接比较函数对象
2.3 基于信号槽的观察者模式变体
信号槽机制是观察者模式的一种高级变体,在Qt等框架中广泛应用。我们可以用现代C++实现一个轻量级的信号槽系统:
cpp复制#include <map>
#include <functional>
#include <memory>
class Signal {
private:
using SlotID = size_t;
std::map<SlotID, std::function<void(const std::string&)>> slots_;
SlotID nextID_ = 0;
public:
template <typename Callable>
SlotID connect(Callable&& callable) {
SlotID id = nextID_++;
slots_.emplace(id, std::forward<Callable>(callable));
return id;
}
void disconnect(SlotID id) {
slots_.erase(id);
}
void emit(const std::string& message) {
for (auto& [id, slot] : slots_) {
slot(message);
}
}
};
// 使用示例
Signal signal;
auto id = signal.connect([](const std::string& msg) {
std::cout << "Slot received: " << msg << std::endl;
});
signal.emit("Hello Signal/Slot!");
signal.disconnect(id);
这种实现的特点:
- 每个连接返回唯一ID,便于精确控制连接生命周期
- 使用map存储槽函数,插入/删除效率更高
- 完全解耦信号发射器和接收器
3. 性能优化与特殊场景变体
3.1 事件队列与异步通知
在高性能场景中,直接同步通知可能影响系统响应性。我们可以引入事件队列实现异步通知:
cpp复制#include <queue>
#include <thread>
#include <condition_variable>
class AsyncEventDispatcher {
private:
std::queue<std::string> eventQueue_;
std::vector<std::function<void(const std::string&)>> handlers_;
std::mutex queueMutex_;
std::condition_variable cv_;
std::atomic<bool> running_{true};
std::thread workerThread_;
void processEvents() {
while (running_) {
std::unique_lock lock(queueMutex_);
cv_.wait(lock, [this] { return !eventQueue_.empty() || !running_; });
if (!running_) break;
auto message = std::move(eventQueue_.front());
eventQueue_.pop();
lock.unlock();
for (auto& handler : handlers_) {
handler(message);
}
}
}
public:
AsyncEventDispatcher() : workerThread_(&AsyncEventDispatcher::processEvents, this) {}
~AsyncEventDispatcher() {
running_ = false;
cv_.notify_all();
workerThread_.join();
}
void postEvent(std::string message) {
std::lock_guard lock(queueMutex_);
eventQueue_.push(std::move(message));
cv_.notify_one();
}
void registerHandler(std::function<void(const std::string&)> handler) {
std::lock_guard lock(queueMutex_);
handlers_.push_back(std::move(handler));
}
};
这种变体适用于:
- GUI系统需要避免阻塞主线程
- 高吞吐量事件处理系统
- 需要事件顺序保证的场景
3.2 基于发布-订阅的分布式观察者模式
对于分布式系统,我们可以扩展观察者模式为发布-订阅模型:
cpp复制#include <unordered_map>
#include <string>
#include <any>
class PubSubBroker {
private:
std::unordered_map<std::string,
std::vector<std::function<void(const std::any&)>>> topics_;
std::mutex mutex_;
public:
template <typename Callable>
void subscribe(const std::string& topic, Callable&& callback) {
std::lock_guard lock(mutex_);
topics_[topic].emplace_back(std::forward<Callable>(callback));
}
void publish(const std::string& topic, const std::any& message) {
std::vector<std::function<void(const std::any&)>> callbacks;
{
std::lock_guard lock(mutex_);
if (topics_.find(topic) != topics_.end()) {
callbacks = topics_[topic];
}
}
for (auto& callback : callbacks) {
callback(message);
}
}
};
// 使用示例
PubSubBroker broker;
broker.subscribe("sensor.data", [](const std::any& data) {
try {
auto value = std::any_cast<double>(data);
std::cout << "Sensor data: " << value << std::endl;
} catch (const std::bad_any_cast&) {
std::cerr << "Invalid data type" << std::endl;
}
});
broker.publish("sensor.data", std::any{3.14});
这种变体的特点:
- 支持基于主题的消息路由
- 使用std::any实现类型擦除,支持多种消息类型
- 天然适合跨模块、跨进程通信场景
4. 现代C++特性在观察者模式中的应用
4.1 使用智能指针管理观察者生命周期
为了避免内存泄漏和悬挂指针问题,我们可以使用智能指针来管理观察者:
cpp复制#include <memory>
#include <set>
class SafeSubject {
private:
std::set<std::shared_ptr<Observer>> observers_;
std::mutex mutex_;
public:
void attach(std::shared_ptr<Observer> observer) {
std::lock_guard lock(mutex_);
observers_.insert(observer);
}
void detach(std::shared_ptr<Observer> observer) {
std::lock_guard lock(mutex_);
observers_.erase(observer);
}
void notify(const std::string& message) {
std::lock_guard lock(mutex_);
auto observers = observers_; // 创建副本
lock.unlock();
for (auto& observer : observers) {
if (auto ptr = observer.lock()) {
ptr->update(message);
}
}
}
};
关键改进:
- 使用std::shared_ptr自动管理观察者生命周期
- 使用std::set避免重复注册
- 仍然保持线程安全性
4.2 使用可变参数模板支持灵活事件类型
C++11的可变参数模板允许我们创建支持任意参数类型的观察者模式:
cpp复制template <typename... Args>
class GenericObserver {
public:
virtual ~GenericObserver() = default;
virtual void update(Args...) = 0;
};
template <typename... Args>
class GenericSubject {
private:
std::vector<std::shared_ptr<GenericObserver<Args...>>> observers_;
public:
void attach(std::shared_ptr<GenericObserver<Args...>> observer) {
observers_.push_back(observer);
}
void notify(Args... args) {
for (auto& observer : observers_) {
observer->update(args...);
}
}
};
// 使用示例
struct ConcreteObserver : GenericObserver<int, std::string> {
void update(int num, std::string str) override {
std::cout << "Received: " << num << ", " << str << std::endl;
}
};
GenericSubject<int, std::string> subject;
subject.attach(std::make_shared<ConcreteObserver>());
subject.notify(42, "answer");
这种实现的优势:
- 支持任意数量和类型的事件参数
- 保持类型安全,避免运行时类型检查
- 可以与std::bind或lambda表达式灵活配合使用
4.3 使用CRTP优化性能
奇异递归模板模式(CRTP)可以消除虚函数调用开销:
cpp复制template <typename Derived>
class ObserverCRTP {
public:
void update(const std::string& message) {
static_cast<Derived*>(this)->onUpdate(message);
}
};
class ConcreteObserverCRTP : public ObserverCRTP<ConcreteObserverCRTP> {
public:
void onUpdate(const std::string& message) {
std::cout << "CRTP observer: " << message << std::endl;
}
};
template <typename Observer>
class SubjectCRTP {
private:
std::vector<Observer*> observers_;
public:
void attach(Observer* observer) {
observers_.push_back(observer);
}
void notify(const std::string& message) {
for (auto observer : observers_) {
observer->update(message);
}
}
};
// 使用示例
SubjectCRTP<ObserverCRTP<ConcreteObserverCRTP>> subject;
ConcreteObserverCRTP observer;
subject.attach(&observer);
subject.notify("Hello CRTP!");
CRTP变体的特点:
- 通过静态多态避免虚函数调用开销
- 编译时绑定,更好的优化机会
- 需要更复杂的模板代码
