CodeWalk

C++观察者模式与信号槽实现

作者:孤独的心 · 2026-05-30 12:55

请用C++实现观察者模式(Observer Pattern),包括主题(Subject)和观察者(Observer)接口、事件通知机制,并对比使用std::function的现代实现与经典虚函数实现。

回答

孤独的心

经典实现(基于虚函数):

class Observer {
public:
  virtual ~Observer() = default;
  virtual void update(const Event&) = 0;
};

class Subject {
  std::vector<std::weak_ptr<Observer>> observers;
public:
  void attach(std::shared_ptr<Observer> obs) {
    observers.push_back(obs);
  }
  void notify(const Event& e) {
    for (auto& wobs : observers) {
      if (auto obs = wobs.lock()) obs->update(e);
    }
    // 清理已销毁的观察者
    std::erase_if(observers, [](auto& w) { return w.expired(); });
  }
};

现代C++实现(基于std::function,更灵活,无需定义Observer接口):

class EventBus {
  std::unordered_map<EventType, std::vector<std::function<void(const Event&)>>> handlers;
  std::mutex mtx;
public:
  template<typename F>
  void subscribe(EventType t, F&& f) {
    std::lock_guard lk(mtx);
    handlers[t].emplace_back(std::forward<F>(f));
  }
  void publish(EventType t, const Event& e) {
    std::lock_guard lk(mtx);
    if (auto it = handlers.find(t); it != handlers.end())
      for (auto& h : it->second) h(e);
  }
};

选择建议

  • 虚函数方法适合强类型、接口固定的场景。
  • std::function方法适合灵活、Lambda驱动的现代C++风格。
  • 线程安全需注意(加锁或使用单线程事件循环)。
  • 注意观察者生命周期管理(weak_ptr或令牌式订阅返回可取消订阅的句柄)。