C++观察者模式IObservable无setScore成员报错 求高效实现方案
问题根因
你遇到的编译报错是基类指针调用派生类独有方法导致的:你在main函数中将CricketMatchScore实例赋值给了IObservable类型的基类指针obl,但IObservable通用接口只定义了订阅、取消订阅、通知三类通用方法,业务相关的setScore是派生类CricketMatchScore的独有方法,基类自然识别不到。
另外需要明确:setScore是被观察者的业务方法,本来就不需要定义到IObserver观察者接口中,你最初的接口拆分逻辑是正确的。
问题1 实现方法
不需要修改任何接口定义,两种方案可选:
- 方案一(推荐,性能无损耗):直接用派生类指针持有实例,取消无意义的向上转型
把main函数中的IObservable * obl = new CricketMatchScore;修改为CricketMatchScore * obl = new CricketMatchScore;即可。基类定义的subscribe等通用方法继承后可正常调用,同时可以直接调用派生类的setScore方法,没有额外性能开销。 - 方案二(适合多类型被观察者统一管理场景):安全向下转型
如果你必须用IObservable基类指针统一管理不同类型的被观察者,可以用dynamic_cast做安全转型:
转型成功才会调用方法,避免类型不匹配引发崩溃。if (auto* match = dynamic_cast<CricketMatchScore*>(obl)) { match->setScore(20); }
问题2 更优的实现方案
现有实现的主要缺陷是接口通用性差、缺少虚析构有内存泄漏风险,推荐两种优化方向:
泛型观察者接口(适配不同业务场景)
把观察者和被观察者接口做成模板类,支持自定义通知参数类型,不用为每类业务单独写接口:
#include <vector> #include <algorithm> #include <iostream> // 通用观察者接口,模板参数为通知传递的参数类型 template<typename NotifyParam> class IObserver { public: virtual void update(const NotifyParam&) = 0; virtual ~IObserver() = default; // 虚析构避免内存泄漏 }; // 通用被观察者接口 template<typename NotifyParam> class IObservable { public: virtual void subscribe(IObserver<NotifyParam>*) = 0; virtual void unsubscribe(IObserver<NotifyParam>*) = 0; virtual void notifyAll() = 0; virtual ~IObservable() = default; }; // 板球比赛被观察者,指定通知参数为int类型的分数 class CricketMatchScore : public IObservable<int> { public: CricketMatchScore() : mScore(0) {} void subscribe(IObserver<int>* obj) override { mObservers.push_back(obj); } void unsubscribe(IObserver<int>* obj) override { auto iter = std::find(mObservers.begin(), mObservers.end(), obj); if (iter != mObservers.end()) { mObservers.erase(iter); } } void notifyAll() override { for (auto& item : mObservers) { item->update(mScore); } } void setScore(int score) { if (mScore != score) { mScore = score; notifyAll(); } } private: std::vector<IObserver<int>*> mObservers; int mScore; }; // 板球信息观察者 class CricInfo : public IObserver<int> { public: void update(const int& score) override { std::cout << score << std::endl; } }; int main() { IObserver<int>* ob1 = new CricInfo; IObserver<int>* ob2 = new CricInfo; // 直接用派生类指针持有 CricketMatchScore* obl = new CricketMatchScore; obl->subscribe(ob1); obl->subscribe(ob2); obl->setScore(20); // 释放内存避免泄漏 delete ob1; delete ob2; delete obl; return 0; }
多事件类型适配(适合复杂业务)
如果同一个被观察者需要触发多种不同事件(比如比赛开始、比赛结束、比分更新),可以定义统一的事件结构体,用std::variant承载不同事件的参数,观察者可以根据事件类型按需处理。
额外优化建议
- 现代C++项目中建议用
std::shared_ptr/std::weak_ptr代替裸指针管理观察者生命周期,避免被观察者销毁后观察者仍持有野指针的问题 - 通知参数用const引用传递,避免大对象拷贝开销
内容的提问来源于stack exchange,提问作者Badhan K
相关产品推荐
相关产品推荐

