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

news2024/10/7 8:23:29

目录

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博客

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

.ini配置文件介绍与解析库使用

【前言】 ini 文件是英文"Initialization"的缩写&#xff0c;即初始化文件。它用来配置特定应用软件以实现对程序初始化或进行参数设置。.ini文件由节(section)、键(key)、值(value)三种模块构成。在windows系统/嵌入式软件中有很多XXX.ini文件&#xff0c;例如Syste…

IDC机房相电压与线电压的关系

380V电动机&#xff08;三相空调压缩机&#xff09;的电流计算公式为&#xff1a;Ⅰ&#xff1d;额定功率&#xff08;1.732额定电压功率因数效率&#xff09;。 功率因数是电力系统的一个重要的技术数据。功率因数是衡量电气设备效率高低的一个系数。功率因数低&#xff0c;说…

通过源码编译安装搭建 LNMP平台

搭建LNMP平台 一. 安装Nginx服务1.1 安装依赖包1.2 创建运行用户1.3 编译安装1.4 优化路径1.5 添加 Nginx 系统服务 二. 安装mysql服务2.1 安装Mysql环境依赖包2.2 创建运行用户2.3 编译安装2.4 修改mysql 配置文件2.5 更改mysql安装目录和配置文件的属主属组2.6 设置路径环境变…

VMware安装Windows11

VMware安装Windows11 嘚吧嘚准备工作VMware下载Windows11下载 VMware安装Windows11VMware配置安装Windows11 嘚吧嘚 最近在搞一些自己感兴趣的东西&#xff0c;需要(临时)安装一些软件来验证&#xff0c;考虑到用完还要卸载&#xff0c;不想把自己的电脑搞得乱七八糟&#x1f…

字节序及IP地址转换

一、主机字节序和网络字节序 1.什么是字节序&#xff1f; 字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序&#xff0c;分为&#xff1a;大端字节序&#xff08;Big endian&#xff09;、小端字节序&#xff08;Little endian&#xff09;。 示例&am…

前端技术双周刊 2023-06-04:React 发布 10 周年整

项目地址&#xff1a;olivewind/weekly 微信公众号&#xff1a;依赖注入 发布时间&#xff1a;2023.06.04 本周内容&#xff1a;资讯x3、开源x4、文章x5 动态 React 圆桌会议 - Server Components, Suspense 和 Actions 为庆祝 React 成立 10 周年&#xff0c;Delba de Oliveir…

MySQL 约束控制

文章目录 约束控制非空约束主键约束默认值约束唯一约束外键约束 约束控制 数据的完整性约束&#xff08;简称“约束”&#xff09;是在表和字段上强制执行的数据检测规则&#xff0c;是为了防止不规范的数据进入数据库。当我们对数据进行 DML 操作时&#xff0c;数据库管理系统…

被面试官上过一课后,我学到的不止是如何答题

写在前面双向奔赴切勿急于否定自己做足功课关于简历关于简历投递常见面试题汇总● 按照一般的面试流程&#xff0c;先来一个自我介绍吧● 你的优点和缺点是什么● 你理解的项目经理是干什么的● 能完整的说一下软件项目的整个流程么● 项目经理和产品经理的区别在哪里● 项目管…

UniAD:实现多类别异常检测的统一模型

来源&#xff1a;投稿 作者&#xff1a;Mr.Eraser 编辑&#xff1a;学姐 论文标题&#xff1a;用于多类异常检测的统一模型 论文链接&#xff1a;https://arxiv.org/abs/2206.03687 论文贡献&#xff1a; 提出UniAD&#xff0c;它以一个统一框架完成了多个类别的异常检测。 …

Jetson 硬件 安装SSD固态作为启动盘以及安装CUDA等

Jetson硬件的自带闪存一般较小&#xff0c;只能安装jetpack等基本的环境&#xff0c;所以需要额外增加SSD固态或SD卡作为存储空间&#xff0c;很明显SSD的读取速度远远大于SD卡&#xff0c;所以为更好发挥出Jetson 的计算性能&#xff0c;我们选择使用SSD固态作为存储 1. 安装…

随机森林原理和性能分析

文章目录 随机森林入门构造随机森林随机森林性能随机森林特点 随机森林入门 决策树入门、sklearn实现、原理解读和算法分析中针对决策树进行了详细的描述&#xff0c;但是其只考虑了一颗决策树的情况。俗话说&#xff0c;三个臭皮匠&#xff0c;顶个诸葛亮。本文将研究如何通过…

C++:深入理解多态,多态实现原理及拓展

文章目录 1. 理解虚表1.1 虚表1.2 验证1.3 子类虚表1.4 相同类不同对象的虚表 2. 静态绑定和动态绑定2.1 静态绑定2.2 动态绑定 3. 多态的实现原理3.1 向上转型3.2 多继承3.3 原理 4. 拓展4.1 构造函数能不能是虚函数4.2 父类和子类的析构函数在底层的命名问题4.3 对象之间无法…

[论文阅读] (30)李沐老师视频学习——3.研究的艺术·讲好故事和论点

《娜璋带你读论文》系列主要是督促自己阅读优秀论文及听取学术讲座&#xff0c;并分享给大家&#xff0c;希望您喜欢。由于作者的英文水平和学术能力不高&#xff0c;需要不断提升&#xff0c;所以还请大家批评指正&#xff0c;非常欢迎大家给我留言评论&#xff0c;学术路上期…

SpringMVC第十一阶段:SpringMVC 拦截器执行源码解析

SpringMVC 拦截器执行源码解析&#xff1a; 1、执行doDispatcher做请求分发处理 1.1、调用getHandler()获取请求处理器&#xff0c;处理器中包含请求的方法和拦截器信息 getHandlerInternal() 根据请求地址获取对应的目标方法getHandlerExecutionChain() 获取请求地址对…

(转载)基于鱼群算法的函数寻优算法(matlab实现)

1 理论基础 1.1 人工鱼群算法概述 人工鱼群算法是李晓磊等人于2002年提出的一类基于动物行为的群体智能优化算法。该算法是通过模拟鱼类的觅食、聚群、追尾、随机等行为在搜索域中进行寻优&#xff0c;是集群体智能思想的一个具体应用。生物的视觉是极其复杂的&#xff0c;它…

Java006——对第一个Java程序HelloWorld的简单介绍

一、HelloWorld.java程序整体认识 public class HelloWorld { //创建一个名字叫HelloWorld的类&#xff08;Java中的类叫class&#xff09;public static void main(String[] args) {//主程序入口&#xff0c;类似C语言main函数System.out.println("He…

python之scipy.signal重采样

前言 在复现LiftingNet过程中&#xff0c;了解到作者对于不同转速设备的机械信号进行重采样来矫正转速&#xff0c;也就是固定长度的样本包含了相同旋转周期的设备信息&#xff0c;而非相同时间长度。po一下原文&#xff1a; 这里其实用到了阶次分析的原理&#xff0c;该样本…

【vue】生命周期,组件,插槽,依赖注入,实现分页器组件,异步组件,keep-alive

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录 生命周期总结组件例子轮播图组件 在组件中使用v-modelMixin插槽具名插槽实现一个分页器 依赖注入…

【软件测试】软件测试总结笔记(1)

软件测试理论总结 1.Introduction1.1 What is Software Bug1.2 Tester的职责和目标其他概念软件测试的分类 2.软件开发生命周期Software Development ProcessSoftware Development Lifecycle ModelsTDD - Test-Driven Development测试驱动开发&#xff08;一种敏捷开发&#x…

城市微博签到数据分享地址解码与纠偏教程

收录于合集 #开源4个 #GIS34个 #社交媒体2个 #大数据4个 哈喽大家好&#xff0c;我又来啦&#xff01;最近一直有小伙伴私戳问我要 签到数据&#xff0c;本着 开源共享的精神&#xff0c;我这次给大家分享中国多个城市2022年9月的匿名签到数据&#xff0c;欢迎大家点击在看…