C++设计模式——单例模式

2023-11-17

我们应该知道,C++中有21种设计模式,常见的有单例模式、迭代器模式、工厂模式、抽象工厂模式、观察者模式。今天我们先来说一下单例模式。

单例模式(Singleton)是设计模式中最为简单、最为常见、最容易实现的模式。单例模式就是怎样去创建一个唯一的变量(对象),即类只能实例化一个对象

1.单例模式实现思路:

1.屏蔽构造函数:

屏蔽生成对象的方法(私有化):将构造函数和拷贝构造函数写在私有访问限定符下,拷贝构造函数可以只写声明。

2.在类中公有访问限定符下提供一个生成对象的接口:

该接口不能依赖对象调用,即不能返回类类型的指针或引用。所以要用静态方法返回,提供一个静态方法来让外界获取对象实例

单例模式分为两种:

饿汉模式:先把对象(面包)创建好,等我要用(吃)的直接直接来拿就行了。

因为饿汉模式可能会造成资源浪费的问题,所以就有了懒汉模式

懒汉模式:先不创建类的对象实例,等你需要的时候我再创建。

2.饿汉模式的实现

class SingleTon//饿汉
{
public:
	static SingleTon* getInstance()
	{
		return psingle;
	}
private:
	SingleTon(){}
	SingleTon(const SingleTon&);//拷贝构造
	static SingleTon* psingle;//指针标识唯一对象
};
SingleTon* SingleTon::psingle=new SingleTon();//静态成员变量在类外进行初始化

静态成员变量初始化时生成的唯一对象是在程序加载时就已经生成好了,即main函数执行前就生成好了,所以一定是线程安全的
因为线程是进程执行过程中产生的,主函数执行前唯一对象已经生成了。

它的缺点就是浪费资源,可能它生成的对象后面就没用,造成资源的浪费。

3.懒汉模式的实现

 

class SingleTon//懒汉
{
public:
	static SingleTon* getInstance()
	{
		if(psingle==NULL)
		{
			psingle=new SingleTon();//用的时候才生成对象
		}
        return psingle;
	}
private:
	SingleTon(){}
	SingleTon(const SingleTon&);
	static SingleTon* psingle;//指针标识唯一对象
};
SingleTon* SingleTon::psingle=NULL;

懒汉模式又称延时加载,线程不安全。所谓线程不安全指:当唯一实例尚未创建时,如果有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。

《改进1》双重锁机制的单例模式

为了保证线程安全,可在生成对象前加锁,对象生成后解锁,但加锁解锁产生的开销,会导致效率变差,又线程不安全是只有在第一次生成对象时才会发生,所以我们只在第一次生成对象时进行加锁和解锁就行,即在外层再加个判断语句。这就是双重锁机制的单例模式,如下:

class SingleTon//懒汉
{
public:
	static SingleTon* getInstance()
	{
		if (psingle == NULL)
		{
			//lock();
			if (psingle == NULL)
			{
				psingle = new SingleTon();
			}
			//unlock();
		}
		return psingle;
	}
private:
	SingleTon(){}
	SingleTon(const SingleTon&);
	static SingleTon* psingle;//标识唯一对象;
};
SingleTon* SingleTon::psingle = NULL;

两次if()判空的作用

第一次判断singleton是否为null:

  第一次判断是在Synchronized同步代码块外进行判断,由于单例模式只会创建一个实例,并通过getInstance方法返回singleton对象,所以,第一次判断,是为了在singleton对象已经创建的情况下,避免进入同步代码块,提升:效率。

第二次判断singleton是否为null:

  第二次判断是为了避免以下情况的发生。 
  (1)假设:线程A已经经过第一次判断,判断singleton=null,准备进入同步代码块. 
  (2)此时线程B获得时间片,由于线程A并没有创建实例,所以,判断singleton仍然=null,所以线程B创建了实例singleton。 
  (3)此时,线程A再次获得时间片,犹豫刚刚经过第一次判断singleton=null(不会重复判断),进入同步代码块,这个时候,我们如果不加入第二次判断的话,那么线程A又会创造一个实例singleton,就不满足我们的单例模式的要求,所以第二次判断是很有必要的。

这样就既解决了饿汉模式的资源浪费问题又解决了懒汉模式的线程不安全问题。

但同时双重锁机制的单例模式又会出现新的问题:构造的唯一对象可能不是完整的。因为计算机系统中为了提高计算机系统性能,编译器、处理器、缓存会对程序指令和数据进行重排序,而对象的初始化操作并不是一个原子操作(可能会被重排序)

实例化一个对象可分为三个步骤:

(1)分配内存空间

(2)初始化对象(调用构造)

(3)将定义的对象的指针变量指向刚分配的内存空间(相当于‘=’赋值操作,使之对应起来)

上述步骤可能会重排序,变成:

(1)分配内存空间

(2)将定义的对象的指针变量指向刚分配的内存空间

(3)初始化对象

因此可能存在这种情况:一个线程正在构造对象的过程中(构造方法还没有执行完),另一个线程检查时看见了psingle为非NULL。即对象可能被非安全发布(对象并不完整就被其他线程使用)。

《改进2》使用volatile关键字禁止编译器优化

进一步优化:可以在使用volatile关键字禁止编译器进行优化,此时重排序被禁止,所有的写操作都将发生在读操作之前。

private:

volatile static SingleTon* psingle;//标识唯一对象;

至此,双重锁机制就可以完美工作了。

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++设计模式——单例模式 的相关文章

  • C++_生成随机字符串

    include
  • 每天都在谈SOA和微服务,但你真的理解什么是服务吗?

    近几年来 我一直从事着和面向服务相关的底层软件研发工作 逐渐的形成了一些自己的看法 其中我觉得比较重要的看法就是服务需要一个更准确细致的定义 简单来说 服务的本质就是行为 业务活动 的抽象 为了更好的阐述新服务的概念 并方便与传统的SOA中
  • 侯捷系列:c++面向对象高级编程(上)

    文章目录 基于对象的程序设计 不带有指针成员变量的类 以复数类 Complex 为例 头文件的结构 访问级别 函数设计 内联函数 构造函数 常量成员函数 参数的值传递和引用传递 返回值的值传递和引用传递 友元 操作符重载 在类内声明 pub
  • C++知识积累:内存对齐理解

    为什么要进行内存对齐 这是因为CPU的读取总是对齐的 举个例子 假设CPU是32位的 那么CPU每次读取的4字节数据的首地址都是4的倍数 也就是说 内存中数据首地址为4的倍数时 CPU一次操作就可以完成数据读取 假设有一个int型四字节大小
  • 范围for语句

    C 新标准提供的范围for语句 这种语句遍历给定序列中个元素并对序列中每一个值执行某种操作 其语法形式是 for declaration expression statement 其中 expression 部分是一个对象 用于表示一个序列
  • android通过JNI用C/C++创建本地文件

    通过jni在本地创建文件 1 在android studio创建基本的jni工程 并且在APP界面成功显示 Hello from C 不会的可以看android studio使用jni 2 在native lib cpp文件中创建文件 为了
  • Vc/MFC中自定义消息及其PostMessage触发使用

    http blog csdn net ztz0223 article details 2058402 http blog csdn net a8082649 article details 7733527 http bbs csdn net
  • std::nth_element bug引起的crash问题

    1 源码 auto less compare const MirroringGroup mg1 const MirroringGroup mg2 gt bool return mg1 usage lt mg2 usage std nth e
  • c/c++入门教程 - 1.基础c/c++ - 1.0 Visual Studio 2019安装环境搭建

    推荐视频课程 https www bilibili com video BV1et411b73Z p 2 已投币三连 b站果然是个学习的网站 本来是想在linux环境下运行QT 于是先学了几个月linux嵌入式驱动开发 后来发现太底层了 与
  • C/C++ 引用作为函数的返回值

    语法 类型 函数名 形参列表 函数体 特别注意 1 引用作为函数的返回值时 必须在定义函数时在函数名前将 2 用引用作函数的返回值的最大的好处是在内存中不产生返回值的副本 代码来源 RUNOOB include
  • 如何学好C语言的数据结构与算法?

    C语言的数据结构与算法 难就难在链表 学会了链表 可能后面就一点都不难了 书籍推荐 数据结构与算法分析 C语言描述版 要深入学习的话可以选择这本书 因为针对链表的讲解是比较详细的 所以可以很快理解链表 跟着书上一点点实现基本操作 增删改查
  • C++中的RTTI

    文章目录 dynamic cast运算符 指针类型的dynamic cast 引用类型的dynamic cast typeid运算符 使用RTTI type info类 参考资料 RTTI Runtime Type Information
  • ATL字符串转换宏

    有比MultiByteToWideChar和WideCharToMultiByte更简单的字符串转换宏 你相信吗 头文件 d program files microsoft visual studio 8 vc atlmfc include
  • Trace Function Enter, Exit and Leave

    http developer nokia com community wiki Trace Function Enter Exit and Leave
  • 【C/C++】 - Linux下查找函数头文件 以及 man命令拓展

    背景 比如现在需要找C语言 sleep函数的头文件 使用man来查找 可以先man sleep 可以发现出来的默认是sleep 1 是一个User Commands 明显不是我们需要的 这里提示了 看sleep 3 那我们查看下sleep
  • 检查内存泄露

    自己编写的视频处理程序出现了一个问题 每帧的运行时间随着运行时间在不断增长 很大可能是出现了内存泄露 于是学习了一些查看内存泄露的方法 做了两种尝试 一是VS自带的DEBUG下的检测 view pl html view plain copy
  • enable_shared_from_this使用介绍

    文章目录 enable shared from this定义 使用场合 源码实现 注意 enable shared from this定义 定义于头文件 template lt class T gt class enable shared
  • C/C++编程:令人印象深刻的高级技巧案例

    C C 编程语言在软件开发领域有着悠久的历史 由于其高效 灵活和底层访问能力 至今仍然被广泛应用 本文将介绍一些在C C 编程中令人印象深刻的高级技巧 帮助读者提升编程水平 更加高效地使用这两种强大的编程语言 一 指针运算与内存管理 C C
  • C 语言运算符详解

    C 语言中的运算符 运算符用于对变量和值进行操作 在下面的示例中 我们使用 运算符将两个值相加 int myNum 100 50 虽然 运算符通常用于将两个值相加 就像上面的示例一样 它还可以用于将变量和值相加 或者将变量和另一个变量相加
  • C 语言文件读取全指南:打开、读取、逐行输出

    C 语言中的文件读取 要从文件读取 可以使用 r 模式 FILE fptr 以读取模式打开文件 fptr fopen filename txt r 这将使 filename txt 打开以进行读取 在 C 中读取文件需要一点工作 坚持住 我

随机推荐

  • Java设计模式

    一 采用设计模式的目的 二 设计模式的七大设计原则 三 常见的设计模式
  • 通过命令行开启和关闭oracle

    1 启动oracle10g 建一个以bat结尾的文件 内容为 net start OracleServiceORCL net start OracleORADB10gTNSListener 2 关闭oracle10g 建一个以bat结尾的文
  • 西门子PLC中如何解决流量累积问题

    现在上位机系统中很多要求具备流量计的流量累计功能 由此引出的几个问题 期望与大家分享 问题1 自行编写流量累计程序 自行编写流量累计程序的原理 其实就是积分的最原始算法概念 把单位小间隔时间内的瞬时流量乘以单位间隔时间 得到单位小间隔时间内
  • kafka详解及集群环境搭建

    一 kafka详解 安装包下载地址 https download csdn net download weixin 45894220 87020758 1 1Kafka是什么 1 Kafka是一个开源消息系统 由Scala写成 是由Apac
  • layout注意事项_注意事项

    layout注意事项 In the post we will look at how you can orchestrate real time customer journeys with Adobe s Journey Orchestr
  • 【华为OD统一考试B卷

    华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一修改为OD统一考试 A卷 和OD统一考试 B卷 你收到的链接上面会标注A卷还是B卷 请注意 根据反馈 目前大部分收到的都是
  • from: can't read /var/mail/xxx 解决方法

    在执行一个发包脚本的时候 遇到了如下问题 from can t read var mail scapy all 原因 脚本是没有问题的 但它并不是可以被python执行的可执行文件 解决方法 1 使用python 脚本名字 命令执行脚本 2
  • k210自学快速入门(附网址)

    一 获得下载工具IDE https dl sipeed com MAIX MaixPy ide 安装和配置见该博客 http t csdn cn 464lj 二 下载固件 1 用于程序的执行下载站 Sipeed 注意 minimum是最小版
  • sqlalchemy create_engine关于连接池的几个参数

    pool size 设置连接池中 保持的连接数 初始化时 并不产生连接 只有慢慢需要连接时 才会产生连接 例如我们的连接数设置成pool size 10 如果我们的并发量一直最高是5 那么我们的连接池里的连接数也就是5 当我们有一次并发量达
  • 级联菜单的动态实现(数据库绑定)

    效果预览 数据库设计 第一类型 第二类型 第三类型 Controller层代码 RequestMapping noFilterGetAllInfo public void noFilterGetAllInfo Integer firstSe
  • 【调试经验】MySQL - fatal error: mysql/mysql.h: 没有那个文件或目录

    机器环境 Ubuntu 22 04 3 LTS 报错问题 在编译一个项目时出现了一段SQL报错 CGImysql sql connection pool cpp 1 10 fatal error mysql mysql h 没有那个文件或目
  • python推荐系统学习笔记(5)——基于图的模型推荐算法

    python推荐系统学习笔记 5 基于图的模型推荐算法 2 1 用户行为数据的二分图表示 为可以把基于邻域的模型看作基于图的模型的简单形式 用户物品二分图模型 对于数据集中每一个二元组 u i 图中都有一套对应的边e vu vi 其中vu属
  • java listnode 合并链表_java实现链表合并

    输入两个单调递增的链表 输出两个链表合成后的链表 当然我们需要合成后的链表满足单调不减规则 最容易想到的就是新建一个链表 一个一个将节点连接到新链表中 代码 public ListNode Merge ListNode list1 List
  • Linux C/C++解析xls

    libxls作为开源库 支持在Linux C C 环境下解析 读 xls文件 github提供了源码 https github com libxls libxls 但是github的源码需要一堆辅助工具 才能够编译出libxls的C静态库
  • C++中while循环中cin语句被跳过问题解析

    今天在写代码的时候 遇到了一个非常奇怪的问题 while true int select cout lt lt 请输入查找的方式 lt lt endl cout lt lt 1 按职工编号查找 lt lt endl cout lt lt 2
  • 学习数据数据结构的意义

    来源 我是码农 转载请保留出处和链接 本文链接 http www 54manong com id 18 什么是数据结构 为什么要学习数据结构 数据结构是否是一门纯数学课程 它在专业课程体系中起什么样的作用 我们要怎么才能学好数据结构 相信同
  • TCP建立连接三次握手和释放连接四次握手

    TCP建立连接三次握手和释放连接四次握手 尊重原创 转载请注明出处 http blog csdn net guyuealian article details 52535294 在谈及TCP建立连接和释放连接过程 先来简单认识一下TCP报文
  • 哪里期货开户低手续费高交返

    国内商品期货开户流程和股票开户的流程很像似 没有开过期货户的 想要开期货户只需要知道这个就行了 可以考虑传统方式去营业部线下开户 但是线下开户弊端手续费较高 地区性垄断 前些年网上开户还没普及时 基本都采用线下开户的方式 随着互联网时代的到
  • QTextCodec中的setCodecForTr等终于消失了 (Qt5)

    在Qt4中 国内很多新手都喜欢 不分青红皂白地使用如下3行代码 QTextCodec setCodecForTr QTextCodec setCodecForCStrings QTextCodec setCodecForLocale 尽管之
  • C++设计模式——单例模式

    我们应该知道 C 中有21种设计模式 常见的有单例模式 迭代器模式 工厂模式 抽象工厂模式 观察者模式 今天我们先来说一下单例模式 单例模式 Singleton 是设计模式中最为简单 最为常见 最容易实现的模式 单例模式就是怎样去创建一个唯