跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 观察者设计模式核心原理与代码实现

C++ 中的观察者设计模式,阐述了其一对多的核心逻辑及四个核心角色(抽象观察者、具体观察者、抽象被观察者、具体被观察者)。提供了基于 C++11 的完整代码实现,涵盖类设计、内存管理(虚析构)、观察者列表管理及推/拉模式对比。文章还分析了 Qt 信号槽、GUI 事件及日志系统等实际应用场景,强调了该模式在解耦和扩展性方面的优势。

RefactorPro发布于 2026/3/30更新于 2026/10/18127 浏览
C++ 观察者设计模式核心原理与代码实现

Observer 模式概述

Observer 模式是行为型设计模式的核心之一,核心逻辑是一对多依赖:当被观察者(Subject)的状态变化时,所有注册的观察者(Observer)会被自动通知并执行更新操作。

用生活场景类比:

  • 被观察者(Subject):电商平台的商品库存
  • 观察者(Observer):库存预警系统、用户订阅提醒、商家后台
  • 逻辑:商品库存低于阈值(状态变化),所有关联的观察者都会收到通知并执行对应操作。

核心角色

C++ 中通过抽象类(纯虚函数) 定义接口,具体类实现接口,核心角色分为 4 个:

  1. Observer(抽象观察者):纯虚类,定义 update 更新接口。
  2. ConcreteObserver(具体观察者):实现 update,处理具体的更新逻辑。
  3. Subject(抽象被观察者):纯虚类,定义 attach(添加观察者)、detach(移除观察者)、notify(通知观察者)接口。
  4. ConcreteSubject(具体被观察者):实现 Subject 接口,维护观察者列表和自身状态,状态变化时触发 notify。

C++ 代码实现

以「商品库存监控」为例,代码包含完整的类设计、内存管理(C++ 核心)和测试逻辑:

#include <iostream>
#include <vector>
#include <algorithm> // 用于 find/erase

// ---------------------- 抽象层 ----------------------
// 前向声明:解决循环依赖问题(Observer 需要知道 Subject,反之亦然)
class Subject;

// 抽象观察者
class Observer {
public:
    // 纯虚更新接口:接收被观察者的状态(库存)
    virtual void update(int stock) = 0;
    // 虚析构:确保子类析构被正确调用(C++ 内存管理关键)
    virtual ~Observer() = default;
};

// 抽象被观察者
class Subject {
public:
    // 添加观察者
    virtual void attach(Observer* observer) = 0;
    // 移除观察者
    virtual void detach(Observer* observer) = 0;
    // 通知所有观察者
    virtual void notify() = 0;
    // 虚析构
    virtual ~Subject() = default;
};

// ---------------------- 实现层 ----------------------
// 具体被观察者:商品库存管理
class ProductStock : public Subject {
private:
    std::vector<Observer*> observers_; // 观察者列表
    int stock_;                        // 核心状态:库存数量

public:
    ProductStock(int init_stock) : stock_(init_stock) {}

    // 添加观察者:避免重复添加
    void attach(Observer* observer) override {
        if (std::find(observers_.begin(), observers_.end(), observer) == observers_.end()) {
            observers_.push_back(observer);
            std::cout << "库存系统:添加观察者 " << typeid(*observer).name() << std::endl;
        }
    }

    // 移除观察者
    void detach(Observer* observer) override {
        auto it = std::find(observers_.begin(), observers_.end(), observer);
        if (it != observers_.end()) {
            observers_.erase(it);
            std::cout << "库存系统:移除观察者 " << typeid(*observer).name() << std::endl;
        }
    }

    // 通知所有观察者:遍历列表调用 update
    void notify() override {
        std::cout << "\n库存系统:库存变化,通知所有观察者!" << std::endl;
        for (auto observer : observers_) {
            observer->update(stock_);
        }
    }

    // 更新库存:状态变化时触发通知
    void setStock(int new_stock) {
        if (new_stock != stock_) {
            // 仅状态变化时通知
            stock_ = new_stock;
            notify();
        }
    }

    // 获取当前库存(拉模式可选)
    int getStock() const { return stock_; }
};

// 具体观察者 1:库存预警系统
class StockAlarm : public Observer {
private:
    int alarm_threshold_; // 预警阈值

public:
    StockAlarm(int threshold) : alarm_threshold_(threshold) {}

    void update(int stock) override {
        std::cout << "库存预警系统:当前库存 " << stock << " 件";
        if (stock < alarm_threshold_) {
            std::cout << " → 低于预警阈值" << alarm_threshold_ << ",触发预警!";
        }
        std::cout << std::endl;
    }
};

// 具体观察者 2:商家后台
class MerchantBackend : public Observer {
public:
    void update(int stock) override {
        std::cout << "商家后台:库存更新为 " << stock << " 件,已同步至系统" << std::endl;
    }
};

// ---------------------- 测试主函数 ----------------------
int main() {
    // 1. 创建被观察者:初始库存 100 件的商品
    ProductStock product(100);

    // 2. 创建观察者:预警阈值 50 件的预警系统、商家后台
    StockAlarm alarm(50);
    MerchantBackend backend;

    // 3. 注册观察者
    product.attach(&alarm);
    product.attach(&backend);

    // 4. 更新库存(触发通知):库存降至 60 件
    std::cout << "\n=== 第一次库存更新:60 件 ===" << std::endl;
    product.setStock(60);

    // 5. 再次更新库存(触发通知):库存降至 40 件(低于预警阈值)
    std::cout << "\n=== 第二次库存更新:40 件 ===" << std::endl;
    product.setStock(40);

    // 6. 移除商家后台观察者,再次更新库存
    std::cout << "\n=== 移除商家后台后,库存更新为 30 件 ===" << std::endl;
    product.detach(&backend);
    product.setStock(30);

    return 0;
}
编译与运行
# 编译(g++)
g++ observer.cpp -o observer -std=c++11
# 运行
./observer 
输出结果
库存系统:添加观察者 9StockAlarm
库存系统:添加观察者 15MerchantBackend
=== 第一次库存更新:60 件 ===
库存系统:库存变化,通知所有观察者!
库存预警系统:当前库存 60 件
商家后台:库存更新为 60 件,已同步至系统
=== 第二次库存更新:40 件 ===
库存系统:库存变化,通知所有观察者!
库存预警系统:当前库存 40 件 → 低于预警阈值 50,触发预警!
商家后台:库存更新为 40 件,已同步至系统
=== 移除商家后台后,库存更新为 30 件 ===
库存系统:移除观察者 15MerchantBackend
库存系统:库存变化,通知所有观察者!
库存预警系统:当前库存 30 件 → 低于预警阈值 50,触发预警!

C++ 实现细节

  1. 前向声明:class Subject; 解决 Observer 和 Subject 的循环依赖问题(观察者需要知道被观察者类型,反之亦然)。
  2. 虚析构函数:virtual ~Observer() = default; 是 C++ 的核心规范——确保通过基类指针/引用析构子类对象时,子类析构函数被正确调用,避免内存泄漏。
  3. 观察者列表管理:使用 std::vector 存储观察者指针,通过 std::find 避免重复添加,erase 安全移除。
  4. 状态判断优化:setStock 中判断 new_stock != stock_,仅状态真的变化时才触发通知,避免无效调用。
  5. 类型识别:typeid(*observer).name() 用于打印观察者类型(不同编译器输出格式可能略有差异,如 9StockAlarm 表示 StockAlarm 类)。
推模式和拉模式
  • 示例中是推模式:被观察者主动把 stock 参数推给观察者(update(int stock))。

拉模式(更灵活):观察者主动从被观察者获取状态,只需修改 update 接口:

// 抽象观察者修改
virtual void update(Subject* subject) = 0;

// 具体观察者实现
void update(Subject* subject) override {
    ProductStock* stock_subject = dynamic_cast<ProductStock*>(subject);
    if (stock_subject) {
        int stock = stock_subject->getStock();
        // 业务逻辑...
    }
}

// 被观察者通知时传 this
void notify() override {
    for (auto observer : observers_) {
        observer->update(this);
    }
}

Observer 模式的实际应用

  1. Qt 框架:QObject::connect 实现的信号与槽机制(核心是观察者模式)。
  2. GUI 事件处理:按钮点击、窗口关闭等事件监听(按钮是 Subject,回调函数是 Observer)。
  3. 日志系统:多个日志输出端(控制台、文件、网络)监听日志事件,自动输出。

总结

  1. C++ 实现观察者模式的核心是抽象类定义接口 + 具体类实现,必须注意虚析构和内存管理。
  2. 关键优势:被观察者与观察者松耦合(仅依赖抽象接口),新增观察者无需修改被观察者代码(符合开闭原则)。
  3. 核心逻辑:被观察者维护观察者列表,状态变化时遍历列表调用 update,实现自动通知。

目录

  1. Observer 模式概述
  2. 核心角色
  3. C++ 代码实现
  4. 编译与运行
  5. 编译(g++)
  6. 运行
  7. 输出结果
  8. C++ 实现细节
  9. 推模式和拉模式
  10. Observer 模式的实际应用
  11. 总结

更多推荐文章

查看全部
  • 微软暑期实习面试全解析:流程、考点与岗位查询
  • OpenClaw 2026.2.22-beta.1 版本更新:支持 Mistral 模型与语音记忆优化
  • Qwen3-VL WebUI 本地部署与核心功能实战指南
  • Virt A Mate (VAM) 虚拟现实模拟软件功能介绍
  • PyFlink 依赖配置指南:JAR、Python 包与虚拟环境集成
  • Pandoc 跨平台安装与配置指南:Windows/macOS/Linux
  • Spring Boot 4.0 正式发布:架构范式跃迁与核心特性解析
  • 前端视角的 API 设计最佳实践
  • 常见漏洞扫描工具及 Nmap 使用指南
  • AI 辅助开发 SpringBoot 在线图书借阅平台实践
  • Ubuntu 22.04 安装英伟达显卡驱动及 CUDA 配置
  • Redis 核心概念与常用数据结构解析
  • 宇树 Unitree 机器人 ROS 2 环境部署指南 (Go2/B2/H1) (Humble)
  • AI 驱动的自动化运维机器人:从数字劳动力到智能协作者
  • YOLO11 基于 DroneVehicle 数据集的无人机视角车辆目标检测
  • Ubuntu 24.04 离线部署 Ollama 及模型导入实战指南
  • 前端微前端架构:告别巨石应用
  • WSL2 + Ubuntu 24.04 + Docker Desktop 本地开发环境搭建
  • Rust 使用迭代器适配器简化代码实现
  • Qwen3-VL 结合 LLaMA-Factory 实现 Grounding 任务 LoRA 微调

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online