单例模式C++实现和观察者模式C++实现

目录

1、单例模式介绍

2、单例代码实现

2.1  static介绍

2.2 C++中static的三种用法:

(1)静态局部变量

(2)静态成员变量

(3)静态成员函数

3、观察者模式介绍

4、观察者代码实现


1、单例模式介绍

      单例模式是属于设计模式中的创建型模式分类(将对象的创建和使用相互分离)。

      单例模式使用的场景是:在一个进程中,只有一个实例对象。该实例构造函数一般声明为私有类,类外不可访问。通过提供访问实例对象的公共函数来访问该实例对象。

      单例模式优点:保证整个程序中只有一个实例类,既保证了数据的唯一性,又节省了空间。

单例的实现方式有:

(1)懒汉式。              (使用时进行实例化。存在线程安全问题)

(2)静态局部变量。    (使用时进行实例化。不存在线程安全问题)

(3)饿汉式。                (单例类创建的时候进行实例化。不存在线程安全问题)

2、单例代码实现

推荐使用静态局部变量实现单例,具体的代码如下:

#include <iostream>

class SingletInstance
{
public:
    static SingletInstance* getInstance()
    {
        static SingletInstance ins;
        return &ins;
    }
private:
    SingletInstance(){std::cout << "SingletInstance Constructot " << std::endl;};
};

int main()
{
    std::cout << "this addr is  " << SingletInstance::getInstance() << std::endl;
    std::cout << "this addr is " << SingletInstance::getInstance() << std::endl;

    return 0;
}

代码运行结果如下:

单例的各种实现方式可参考下面链接:

总结C++单例模式_c++ proto类需要自己释放吗_发如雪Jay的博客-CSDN博客

2.1  static介绍

        C++内存分区可大致分为:栈、堆、全局数据区(静态区)、代码区,staitc修饰的内容属的于全局数据区(静态区),全局数据区中存在的变量包括:全局变量和静态变量存储,生成周期在程序结束时释放。

2.2 C++中static的三种用法:

(1)静态局部变量

      定义:使用static修饰类型的变量,如static int a。生命周期为程序运行周期。

      优点:静态局部变量的作用域外部不可访问,具有较好的安全性。

       注意:只初始化一次,若不赋值,则自动赋值为0,下一次进入该函数时,会自动忽略初始化语句。

代码如下:

void func()
{
    static int a = 10;
    a++;
    std::cout << "a " << a << std::endl;
}

int main()
{

    int cnt = 1;
    while(cnt <= 3)
    {
        func();
        cnt++;
    }
    return 0;
}

运行结果如下:

(2)静态成员变量

定义:使用static修饰的类成员变量,生命周期为程序运行周期。 

注意:每个类的对象的静态成员变量指向的是同一个块地址区域,即数据内容和地址一样。

必须在类对象使用前初始化。

代码如下:

#include <iostream>

class A
{
public:
    static int a;
};


int A::a = 10; //必须使用前初始化!
int main()
{
    std::cout << "A::a  is " << A::a <<",addr is " <<  &(A::a) << std::endl;
    
    / 下面访问静态成员变量的方法不使用,只为了说明,一般使用A::a访问静态成员变量。 //
    A obj1;
    A obj2;
    A obj3;
    std::cout << "A::a  is " << obj1.a << ",addr is " << &(obj1.a) <<std::endl;
    std::cout << "A::a  is " << obj2.a << ",addr is " << &(obj2.a) <<std::endl;
    std::cout << "A::a  is " << obj3.a << ",addr is " << &(obj3.a) <<std::endl;
    //
    return 0;
}

运行结果如下:

(3)静态成员函数

  定义:在成员函数前面加上static修饰符,如:static void fun(){};

  访问:用类名::函数名进行访问

 静态成员函数与非静态成员函数区别:非静态成员函数可以任意地访问静态成员函数和静态数据 成员,静态成员函数不能访问非静态成员函数和非静态数据成员。

3、观察者模式介绍

 观察者模式属于设计模式中的行为型模式分类(关注对象的行为或者交互方面的内容)。

观察者模式使用场景是 描述多个观察者(Observer)订阅一个被观察者的对象状态;当被观察者状态变化时,被观察者会通知所有订阅的观察者对象,让其接收取到状态变化信息。

应用:Qt框架中数据间的通信机制是信号槽,信号槽机制就是观察者模式_百度百科 (baidu.com)应用的体现。

观察者模式也被称为发布者-订阅模式。观察者模式是对象间一对多的关系描述,类似广播。

4、观察者代码实现

代码设计的观察对象如下:

(1)定义一个抽象的被观察者(AbsTarget)类,该类有订阅、取消订阅、通知属性变化、设置数据的接口。

(2)被观察对象(Target1)继承于AbsTarget,对应的观察者对象有Observer1、Observer2。

(3)被观察对象(Target2)继承于AbsTarget,对应的观察者对象Observer3。

(4) 定义一个抽象的观察者(AbsObserver)类,该类有接收数据变化的接口,该接口为了应对不同的数据类型,接收的数据类型定义为void*。

(5)观察者1(Observer1)继承于AbsObserver类;观察者2(Observer2)继承于AbsObserver类,Observer1和Observer2接收数据变化的数据结构为同一种。

(6)观察者3(Observer3)继承于AbsObserver类,Observer3接收的数据结构为一种。

具体代码实现如下:

#include <iostream>
#include <list>
#include <string>
struct Data1
{
    int n1;
    float f1;
};

struct Data2
{
    int n1;
    std::string strname;
};
//观察者
class AbsObserver
{
public:
    AbsObserver()=default;
    virtual ~AbsObserver(){};
    virtual void receiveData(void* pThis)=0;    //使用void*来适应不同的数据类型
};

//具体观察者1
class Observer1:public AbsObserver
{
public:
    Observer1()=default;
    ~Observer1(){};

    virtual void receiveData(void* pThis)override{
        Data1* data1 = (Data1*)pThis;
        std::cout << "  Observer1 receive ";
        std::cout << "f1: " <<  data1->f1 <<  ", n1: " <<data1->n1 << std::endl;
    }
};

//具体观察者2
class Observer2:public AbsObserver
{
public:
    Observer2()=default;
    ~Observer2(){};

    virtual void receiveData(void* pThis)override{
        Data1* data1 = (Data1*)pThis;
        std::cout << "  Observer2 receive ";
        std::cout << "f1: " <<  data1->f1 <<  ", n1: " <<data1->n1 << std::endl;
    }
};

class Observer3:public AbsObserver
{
public:
    Observer3()=default;
    ~Observer3(){};

    virtual void receiveData(void* pThis)override{
        Data2* data1 = (Data2*)pThis;
        std::cout << "  Observer3 receive ";
        std::cout << "strname: " <<  data1->strname <<  ", n1: " <<data1->n1 << std::endl;
    }
};


//被观察目标
class AbsTarget{
public:
    AbsTarget()=default;
    virtual ~AbsTarget(){
        std::cout << "~AbsTarget" << std::endl;
    };

    virtual void Attach(AbsObserver* obj)=0;
    virtual void Detach(AbsObserver* obj)=0;
    virtual void NotifyData()=0;
    virtual void setData(void* data)=0;

};

//具体被观察目标类型1
class Target1:public AbsTarget
{
public:
    Target1()=default;
    virtual ~Target1(){
        std::cout << "~Target1" << std::endl;
        if(m_pObservers.size() >0)
        {
            m_pObservers.clear();
            std::cout << "Target1 clear Data" << std::endl;
        }
    };

    virtual void Attach(AbsObserver* obj)override
    {
        m_pObservers.push_back(obj);
    };
    virtual void Detach(AbsObserver* obj)override{
        m_pObservers.remove(obj);
    };
    virtual void NotifyData()override
    {
        for(auto it:m_pObservers)
        {
            it->receiveData(&m_data1);
        }
    }

    void setData(void* data)override
    {
        Data1* tmp = (Data1*)data;
        m_data1.f1 =tmp->f1;
        m_data1.n1 =tmp->n1;
        std::cout << "Target1 NotifyData************" << std::endl;
        NotifyData();
    }
private:
    std::list<AbsObserver*>m_pObservers;
    Data1 m_data1;
};

//具体被观察目标类型2
class Target2:public AbsTarget
{
public:
    Target2()=default;
    virtual ~Target2(){
        std::cout << "~Target2" << std::endl;
        if(m_pObservers.size() >0)
        {
            m_pObservers.clear();
            std::cout << "Target2 clear Data" << std::endl;
        }
    };

    virtual void Attach(AbsObserver* obj)override
    {
        m_pObservers.push_back(obj);
    };
    virtual void Detach(AbsObserver* obj)override{
        m_pObservers.remove(obj);
    };
    virtual void NotifyData()override
    {
        for(auto it:m_pObservers)
        {
            it->receiveData(&m_data1);
        }
    }

    void setData(void* data)override
    {
        Data2* tmp = (Data2*)data;
        m_data1.strname =tmp->strname;
        m_data1.n1 =tmp->n1;
        std::cout << "Target2 NotifyData************" << std::endl;
        NotifyData();
    }
private:
    std::list<AbsObserver*>m_pObservers;
    Data2 m_data1;
};

void UseTarget1()
{
    AbsTarget *pTarger1 = new Target1();

    AbsObserver* pObserve1= new Observer1();
    pTarger1->Attach(pObserve1);
    AbsObserver* pObserve2= new Observer2();
    pTarger1->Attach(pObserve2);
    Data1 tmp;
    tmp.f1 = 30.06;
    tmp.n1 = 60;
    pTarger1->setData(&tmp);
    pTarger1->Detach(pObserve1);
    pTarger1->setData(&tmp);

    pTarger1->Detach(pObserve2);
    delete  pTarger1;
    pTarger1 = nullptr;
    delete  pObserve1;
    pObserve1 = nullptr;
    delete  pObserve2;
    pObserve2 = nullptr;
}

void UseTarget2()
{
    AbsTarget *pTarger2 = new Target2();

    AbsObserver* pObserve3= new Observer3();
    pTarger2->Attach(pObserve3);
    Data2 tmp;
    tmp.strname = "hello";
    tmp.n1 = 60;
    pTarger2->setData(&tmp);
    pTarger2->Detach(pObserve3);

    delete  pTarger2;
    pTarger2 = nullptr;
    delete  pObserve3;
    pObserve3 = nullptr;

}
int main()
{
    std::cout << "*********** Use Target1, Observer1, Observer2 ***********" << std::endl;
    UseTarget1();
    std::cout << "\n*********** Use Target2, Observer3 ***********" << std::endl;
    UseTarget2();

    return 0;
}

程序运行结果如下:

 关于观察者模式介绍也可参考:

C++行为型模式-实现观察者模式_观察者模式c++实现_herryone123的博客-CSDN博客

附加:

1、设计模式介绍和分类可参考:

C++设计模式介绍与分类_夜雨听萧瑟的博客-CSDN博客

2、static的用法可参考:

c++中static的用法详解_c++中static的作用和用法_「已注销」的博客-CSDN博客

对C语言 static作用——修饰 变量(全局变量/局部变量)、函数_c语言static修饰的局部变量_杰儿__er的博客-CSDN博客

3、对信号槽机制感兴趣的可参考链接:

信号槽机制_夜雨听萧瑟的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/26585.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023年十大最佳自动化测试工具(建议收藏)

Best Automation Testing Tools for 2023 对更快交付高质量软件&#xff08;或"快速质量"&#xff09;的需求要求组织以敏捷&#xff0c;持续集成&#xff08;CI&#xff09;和DevOps方法论来寻找解决方案。测试自动化是这些方面的重要组成部分。最新的《 2018-2019…

使用koa2搭建后端服务器

目录 第一步 第二步 第三步 第四步 第五步 流程说明 1.配置连接数据库 2.添加Schema模型 3.配置路由接口 第一步 npm i -g koa-generator 第二步 koa2 server 第三步 cd server && npm install 第四步 将服务器拖进编译器运行 npm run dev 在浏览器输入…

迅为RK3568开发板2800页手册+220集视频

iTOP-3568开发板采用瑞芯微RK3568处理器&#xff0c;内部集成了四核64位Cortex-A55处理器。主频高达2.0Ghz&#xff0c;RK809动态调频。集成了双核心架构GPU&#xff0c;ARM G52 2EE、支持OpenGLES1.1/2.0/3.2OpenCL2.0、Vulkan 1.1、内高性能2D加速硬件。 内置NPU 内置独立NP…

SpringBoot生成RESTful API文档

由于我一开始学习的SpringBoot是3以上版本&#xff0c;所以我这里用到的也是支持和SpringBoot3能够整合的SpringDoc 这里先说一下&#xff0c;其实SpringDoc就是Swagger3版本&#xff0c;我一开始整合的2版本&#xff0c;比较麻烦况且最后SpringBoot程序都启动不了了&#xff0…

信息竞赛笔记(2)––快速幂

目录 快速幂 定义 分析 代码 递归实现 非递归实现(通用方法) 模意义下取幂 快速幂 定义 快速幂,二进制取幂(Binary Exponentiation,也称平方法),是一个在的时间内计算的小技巧&#xff0c;而暴力的计算需要的时间。 这个技巧也常常用在非计算的场景&#xff0c;因为它可…

yolov5部署到android studio

目录 环境获取demo将pt文件导出为ptl文件修改demo修改PrePostProcessor增加ptl文件并增加类别文件修改MainActivity 大功告成 环境 Ubuntu22.10 Pytorch2.0.1cu117 Android Studio Flamingo | 2022.2.1 Patch 1 获取demo git clone https://github.com/pytorch/android-demo…

day43|动态规划6-完全背包及其应用-零钱兑换II-组合总和IV

完全背包 前情提要&#xff1a; 0-1背包指的是给定背包重量&#xff0c;将物品放入背包中&#xff0c;使得背包中的物品达到最大的价值。&#xff08;每个物品只能往其中放一次&#xff09; 在0-1背包问题中&#xff0c;第二层for循环需要是倒序遍历才可以保证每个物品只使用一…

重估端到端原则

评价技术迭代的旧的定势眼光来自于该技术诞生时。 1970/80/90 年代&#xff0c;相比传输带宽技术&#xff0c;处理器更强。网络协议倾向于字段多&#xff0c;字段小且紧凑&#xff0c;尽可能减少传输量&#xff0c;用 “算法技巧” 等价&#xff0c;如果 TCP 序列号 48 位&…

使用 Docker 部署 Jenkins 代理(主从)控制服务器

自动化是 DevOps 的核心。各种自动化工具和技术真正实现了持续集成和持续交付的概念。这些工具多年来发展迅速&#xff0c;但似乎永远存在的一个名字是Jenkins。 我们不会在这篇文章中讨论 CI-CD 的介绍性概念&#xff0c;也不会浪费时间展示 Jenkins 安装步骤。如果您是 Jenk…

字节面试这么难?6年测开被暴虐.....

前几天我朋友跟我吐苦水&#xff0c;这波面试又把他打击到了&#xff0c;做了快6年软件测试员。。。为了进大厂&#xff0c;也花了很多时间和精力在面试准备上&#xff0c;也刷了很多题。但题刷多了之后有点怀疑人生&#xff0c;不知道刷的这些题在之后的工作中能不能用到&…

【python】之loguru库,好用的日志管理库!

在 Python 中用到日志记录&#xff0c;那就不可避免地会用到内置的 logging标准库 。虽然logging 库采用的是模块化设计&#xff0c;你可以设置不同的 handler 来进行组合&#xff0c;但是在配置上通常较为繁琐&#xff1b;而且如果不是特别处理&#xff0c;在一些多线程或多进…

Nautilus Chain全球行分享会,深圳站圆满举办

在北京时间 6 月 4 日&#xff0c;由 Nautilus Chain 主办的“Layer3 模块化区块链的发展探讨”为主题的全球行活动&#xff0c;在深圳&#xff08;深圳南山区清华研究院&#xff09;顺利举办&#xff0c;本次分享会联合主办方还包括 Stanford Blockchain Accelerator、Zebec …

OpenGL简介

1.简介 一般它被认为是一个API&#xff0c;包含了一系列可以操作图形、图像的函数。然而&#xff0c;OpenGL本身并不是一个API&#xff0c;它仅仅是一个由Khronos组织制定并维护的规范(Specification)。OpenGL规范严格规定了每个函数该如何执行&#xff0c;以及它们的输出值。…

开发一个收废品小程序步骤

随着环保意识的提升和可持续发展的迫切需求&#xff0c;废品回收成为了一个重要的议题。预约上门回收小程序的开发为用户提供了方便、快捷的废品回收服务&#xff0c;促进了废品资源的再利用和环保行动的推进。本文将介绍开发预约上门回收小程序的流程&#xff0c;以帮助开发人…

IDEA启动图片更改替换(2021.1/2022及其之后的版本)

目录 先说2022.1及其之后的版本: 2022.1之前的版本: 2022其他版本修改方法 最近一直在整理接口数据&#xff0c;盯屏幕太久了&#xff0c;然后打开IDEA突然感觉这个启动页面好刺眼&#xff0c;正好整理工作做完了&#xff0c;中午有空就找了下方法,发现了不少坑&#xff0c;…

Linux命令(26)之uptime

Linux命令之uptime 1.uptime介绍 linux命令uptime是用来为用户提供系统从开启到当前运行uptime命令时系统已运行的时长信息&#xff0c;除此之外&#xff0c;还提了系统启动时间&#xff0c;当前登录用户&#xff0c;系统平均负载信息。 2.uptime用法 uptime [参数] uptime…

ChatGPT 提示的艺术 —— 如何编写清晰有效提示指南

ChatGPT 提示的作用 正如我们之前提到的那样&#xff0c;ChatGPT 对话中使用的提示的质量可以显著影响对话的成功。定义清晰的提示可以确保对话保持在正确的轨道上&#xff0c;并涵盖用户感兴趣的主题&#xff0c;从而产生更引人入胜和信息丰富的体验。 那么什么样的 ChatGPT…

Linux进程间通信——管道,共享内存,消息队列,信号量

进程间通信 文章目录 进程间通信进程间通信的方式进程间通信的概念如何实现进程间通信管道什么是管道 进程间怎么通信 匿名管道pipe函数创建管道通信读写特征写慢读快写快读慢写端关闭&#xff0c;读端读完读端关闭&#xff0c;写端&#xff1f; 管道特征 命名管道命名管道特性…

2023接口自动化测试,完整入门篇(超详细~)

一、自动化测试 众所周知&#xff0c;自动化测试已经成为软件项目中不可或缺的测试方法。基于用户交互界面&#xff08;GUI&#xff09;的自动化测试方法具有模拟用户行为和过程可视化的特点&#xff0c;因此受到了广大入门自动化人士的喜爱。诸如&#xff1a;QTP、Selenium等…

BR 5AP1130.156C-000

物料号: 5AP1130.156C-000 描述: 自动化装置面板 15.6" FullHD TFT - 1920 x 1080 像素 (16:9) - 多点触控&#xff08;投射电容&#xff09; - 开关柜安装 - 横向 - 用于 PPC900/PPC2100/PPC3100/ 联接模块 B&R ID 代码0xEC5D许可证 显示屏 类型TFT 彩色对角线…