You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 17:09:04