用纯虚函数定义Observer接口最清晰,即声明virtual void update(const std::string& event) = 0,强制子类实现,避免对象切片和运行时类型擦除,Subject用std::vector管理生命周期。
虚函数接口是 C++ 观察者模式最常用、最符合面向对象语义的方式。关键在于把 update 声明为纯虚函数,强制子类实现,同时避免对象切片和运行时类型擦除问题。
常见错误是直接在基类里提供空的 update 实现(非纯虚),导致派生类忘记重写却无编译报错;或者用 std::function 替代接口,失去静态多态和零开销抽象的优势。
Observer 基类只含一个纯虚函数 virtual void update(const std::string& event) = 0;
Subject 持有 std::vector<:unique_ptr>>,避免裸指针生命周期失控std::move 转移所有权,禁止拷贝 Observer 对象(可删掉拷贝构造/赋值)obs->update(event),多态分发由 vtable 完成,无额外开销
class Observer {
public:
virtual ~Observer() = default;
virtual void update(const std::string& event) = 0;
};
class Subject {
std::vector> observers;
public:
void attach(std::unique_ptr obs) {
observers.push_back(std::move(obs));
}
void notify(const std::string& event) {
for (const auto& obs : observers) {
obs->update(event);
}
}
};
函数指针适合极简场景(比如嵌入式或性能敏感模块),但无法捕获对象状态,必须搭配上下文参数或静态成员函数使用。容易踩的坑是传入普通成员函数地址——它不是自由函数,不能直接转成 void(*)()。
若要绑定对象实例,必须用静态成员 + void* 上下文,或改用 std::function(但已偏离“函数指针”本意)。
立即学习“C++免费学习笔记(深入)”;
void (*)(const char*, void*),第二个参数用于传递 this
void* 上下文,通知时一并传入&MyClass::onUpdate —— 编译失败,成员函数指针与普通指针不兼容using Callback = void (*)(const char*, void*);class SubjectFP { struct Handler { Callback fn; void ctx; }; std::vector
handlers; public: void attach(Callback fn, void ctx) { handlers.push_back({fn, ctx}); } void notify(const char* event) { for (const auto& h : handlers) { h.fn(event, h.ctx); } } };// 使用示例: void log_update(const char e, void obj) { std::cout << "Log: " << e << "\n"; }
如果需要捕获局部变量或快速原型验证,std::function 配合 lambda 最方便。但它底层可能触发堆分配(当闭包对象较大时),且每次调用有间接跳转开销,不适合高频通知路径。
另一个问题是生命周期管理:lambda 捕获局部变量后,若 Subject 生命周期长于该变量,就会变成悬空引用。必须确保捕获的是值([=])或显式延长依赖对象寿命。
std::vector<:function std::string>>
[&obj]() { obj.handle(); },但注意 obj 必须比 Subject 活得久无论用哪种方式,最大的实际问题不是语法怎么写,而是谁负责销毁观察者、何时从 Subject 中移除。C++ 没有弱引用原语,std::weak_ptr 只适用于共享所有权场景,而多数观察者是独占或栈上对象。
虚函数方案中,Subject
持有 unique_ptr 表示强拥有,意味着观察者不能独立于 Subject 存活;函数指针方案则完全无所有权语义,全靠程序员手动保证有效性。这是所有 C++ 观察者实现中最容易出 bug 的地方。
Subject 持有裸指针或引用detach() 接口,允许观察者主动注销(需在 Observer 析构时调用)libsigc++ 或 Qt)替代手写,它们内置了连接管理和自动断连