设计者模式(1)观察者模式 (Observer)C++11实现
admin
2024-04-15 16:35:24
0

意图

观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
该模式属于行为型模式。

参与者

  • Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个或者多个接口,可以增加和删除观察者对象。
  • ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
  • Observer:抽象观察者,是观察者者的抽象类,它定义了一个或者多个更新接口,使得在得到主题更改通知时更新自己。
  • ConcreteObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

优点

  • 观察者模式可以实现表示层和数据逻辑层的分离,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色
  • 观察者模式在观察目标和观察者之间建立一个抽象的耦合
  • 观察者模式支持广播通信

缺点

  • 在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,将所有的观察者都通知到会花费很多时间。如果考虑到性能问题,可以实现一个异步非阻塞的观察者模式,在每次fire subject的时候创建一个新的线程执行代码。

  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

代码实现

#include 
#include 
#include
#include
#includeclass Observer;
typedef std::vector>::iterator Iterator;//迭代器//抽象被观察者
class Subject
{
public:virtual void Attach(std::weak_ptr)=0;//注册观察者对象的接口virtual Iterator Detach(Iterator it)=0;//删除观察者对象的接口virtual void notifyObservers()=0;//告知所有观察者update
};//抽象观察者class Observer
{
public:virtual void update()=0;//更新自身状态
};//被观察者
class ConcreteSubject :public::Subject
{   
public:void Attach(std::weak_ptrs){//vector的add是线程安全的observers_.push_back(s);}Iterator Detach(Iterator it){//线程不安全,要在临界区调用此接口return observers_.erase(it);}void notifyObservers(){lock.lock();Iterator it=observers_.begin();while(it!=observers_.end()){//先尝试将weak_ptr提升为share_ptrstd::shared_ptrobj=it->lock();if(obj)//提升成功,说明该对象未被其他线程析构{obj->update();//更新++it;}else//提升失败,说明该对象已被其他线程析构{//从观察者集合中将已被析构对象删除//因为此处属于临界区,是线程安全的it=Detach(it);}}lock.unlock();}private:mutable std::mutex lock;std::vector> observers_;//观察者集合};//观察者按需求定义即可,需要继承抽象观察者
//以下为例
class Teacher :public::Observer
{
public:void update(){std::cout<<"Teacher is update"<std::cout<<"Teacher is ~"<
public:void update(){std::cout<<"Student is update"<std::cout<<"Student is ~"<

测试

int main()
{ConcreteSubject subject;std::shared_ptr teacher(new Teacher);subject.Attach(teacher);{std::shared_ptr student(new Student);subject.Attach(student);subject.notifyObservers();}subject.notifyObservers();}

执行结果

Teacher is update
Student is update
Student is ~
Teacher is update
Teacher is ~

迭代器失效问题分析

在学习该模型时,遇到了一个bug,问题锁定在notifyObservers()当中,当时的实现如下

void notifyObservers()
{lock.lock();Iterator it=observers_.begin();while(it!=observers_.end()){std::shared_ptrobj=it->lock();if(obj){obj->update();}else{Detach(it);}it++;}
}

当时执行main时,就会报段错误,经过检查不难发现,问题是迭代器失效了

删除导致迭代器失效

这个问题其实很好分析,在一顺序容器当中(vector,queue…等),数据是顺序存储的,当删除一个元素后,内存中的数据会往前移动,以保证数据的紧凑。所以删除一个元素后,该元素后面的其他元素的地址都会发生改变。在删除操作前所持有的迭代器就会有失效的可能。看下面的例子。

#include 
#include
#include
using namespace std;
int main()
{ vectorarr;arr.push_back(1);arr.push_back(2);arr.push_back(3);arr.push_back(4);arr.push_back(5);vector::iterator it=arr.begin()+3;cout<<"删除操作前该迭代器所指向的元素:"<

执行结果如下

删除操作前该迭代器所指向的元素:
4
进行删除操作
删除操作前该迭代器所指向的元素:
5

添加导致迭代器失效

你的添加的操作也是可能导致迭代器失效的。vector的容量是动态的开辟的,当容量不够的话,会进行realloc操作进行扩容,若该数组的后面的空间不足,realloc函数会找一片更大的空间,将原数据拷贝进去。若在此事件发生前,获取一个迭代器,此事件发生后,该迭代器会失效。看下面例子

#include 
#include
#include
using namespace std;
int main()
{ vectorarr;arr.push_back(1);arr.push_back(2);arr.push_back(3);arr.push_back(4);arr.push_back(5);vector::iterator it=arr.begin()+3;cout<<"此事件发生前该迭代器所指向的元素:"<arr.push_back(9);}cout<<"此事件发生后该迭代器所指向的元素:"<

执行结果

此事件发生前该迭代器所指向的元素:
4
此事件发生后该迭代器所指向的元素:
21854

此时该迭代器就指向一个未初始化的空间

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...