0

0

c++中如何实现观察者模式_c++设计模式之观察者模式代码【汇总】

裘德小鎮的故事

裘德小鎮的故事

发布时间:2026-02-03 14:42:10

|

494人浏览过

|

来源于php中文网

原创

Observer接口必须声明虚析构函数,否则多态删除会导致析构不完整、内存泄漏或未定义行为;纯接口也需显式声明,编译器不会自动生成。

c++中如何实现观察者模式_c++设计模式之观察者模式代码【汇总】

Observer 接口必须用虚析构函数

不加 virtual ~Observer() = default; 会导致派生类对象通过基类指针删除时析构不完整,内存泄漏或未定义行为。这是 C++ 观察者模式最常被忽略的底层陷阱。

实际场景中,Subject 通常持有 std::vector<:unique_ptr>>std::vector,而后者更常见(避免强制所有权转移)。无论哪种,只要涉及多态删除,就必须有虚析构。

  • std::unique_ptr → 必须虚析构
  • 用裸指针 Observer* → 仍需虚析构(因为 delete ptr 会调用基类析构)
  • 若 Observer 是纯接口(无数据成员),也仍需显式声明虚析构,否则编译器不会自动生成

std::function + std::vector 实现松耦合 Observer

比起继承接口,用回调函数注册更轻量、无侵入性,适合事件驱动或脚本化扩展场景。但要注意生命周期管理——std::function 捕获局部变量时极易悬垂。

典型错误是把 lambda 捕获了上对象后存进 Subject 的容器里,稍后调用就崩溃。安全做法是只捕获 this 或智能指针,或确保观察者对象比 Subject 活得久。

立即学习C++免费学习笔记(深入)”;

class Subject {
    std::vector> observers_;
public:
    void attach(std::function cb) {
        observers_.push_back(cb);
    }
    void notify(int value) {
        for (auto& cb : observers_) cb(value);
    }
};

// ✅ 安全:捕获 this(假设 ObserverImpl 生命周期可控) struct ObserverImpl { void onValue(int v) { / ... / } void registerTo(Subject& s) { s.attach([this](int v) { this->onValue(v); }); } };

线程安全不是默认选项

std::vector::push_back 和遍历通知都不是原子操作。多线程下并发 attach()notify() 会触发 data race,甚至导致迭代器失效或容器重分配时崩溃。

Dora
Dora

创建令人惊叹的3D动画网站,无需编写一行代码。

下载

常见折中方案是读多写少时用 std::shared_mutex(C++17),或直接加 std::mutex 锁住整个通知流程。但锁粒度太大会降低吞吐,尤其通知链路长时。

  • 避免在 notify() 中调用可能阻塞或长时间运行的回调
  • 若需异步通知,建议把回调转为 post 到线程池,而不是在锁内执行
  • 不要在回调里调用 detach() —— 遍历时修改容器是未定义行为;应改用标记+延迟清理(如 erase-remove idiom)

std::weak_ptr 防止循环引用(当用 shared_ptr 管理 Observer)

如果 SubjectObserver 都用 std::shared_ptr 互相持有,就会形成循环引用,导致对象永远不析构。解决方案是 Subject 改用 std::weak_ptr 存储,每次通知前先 lock()

这会带来额外开销(控制块访问 + 引用计数检查),但能彻底解决悬挂指针和泄漏问题。适用于 Observer 生命周期不可控(比如来自插件模块)的场景。

class Subject {
    std::vector> observers_;
public:
    void attach(std::shared_ptr obs) {
        observers_.push_back(obs);
    }
    void notify() {
        // 注意:这里要拷贝并过滤已销毁的 observer
        std::vector> alive;
        for (auto& w : observers_) {
            if (auto s = w.lock()) alive.push_back(s);
        }
        for (auto& obs : alive) obs->update();
    }
};

C++ 观察者最难的从来不是结构,而是谁负责生命周期、谁触发销毁、谁保证调用时对象还活着——这些不能靠模式图解决,得靠具体容器语义和所有权约定。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

15

2025.11.27

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

208

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

191

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

57

2026.01.05

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1230

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

255

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2191

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

29

2026.01.19

全国统一发票查询平台入口合集
全国统一发票查询平台入口合集

本专题整合了全国统一发票查询入口地址合集,阅读专题下面的文章了解更多详细入口。

4

2026.02.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Django 教程
Django 教程

共28课时 | 3.9万人学习

Excel 教程
Excel 教程

共162课时 | 15.2万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号