本文参考自《大话设计模式》,想借此记录一下对书本内容的理解,并以自己项目为例子采用C++语言进行举例。

概念

单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。通俗点讲,在程序当中创建了一个类,我们希望它仅能被实例化一次,产生一个对象。我们不能靠程序员去控制自己只能实例化一次,这是非常不保险的,我们需要代码内在机制帮助我们去控制这样的行为,这就是设计模式的意义。假设一个项目当中只能存在一个AGV(无人小车)对象。

三个要点

  • 某个类只能有一个实例
  • 它必须自行创建这个实例
  • 它必须自行向整个系统提供对这个实例的访问

方法

1.让new失效

所有类都有构造函数,如果没有编写构造函数,则编译器会使用默认构造函数。首先考虑到的是将构造函数写成private,这样堵死了外界利用new创建此类实例的可能。因为在外界调用new产生实例实质上也是调用构造函数。

public class AGV{private AGV(){}
}

2.建立静态变量

通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的方法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。

我们在类当中去保留这个唯一的实例化对象指针,并提供public函数去实例化/访问它,即设立一个static成员。在类中的static成员,代表所有的对象共用这一个成员,静态函数同理。

构造函数是私有的,但是GetInstance函数是内部成员函数,可以调用私有变量。函数内部首先要判断agvObject是否为空,如果为空,证明之前并没有创建,则通过new实例化一个对象。如果不为空,则证明之前已经创建了,则直接返回agvObject。此函数是获得本类实例的唯一全局访问点。

public class agvClass{private static agvClass* agvObject;private agvClass(){}public static agvClass GetInstance() {if (agvObject == null) {agvObject = new agvClass();}return agvObject;}
}

多线程时的单例

1.问题提出

在多线程时,如果多个线程同时访问GetInstance函数,则有可能造成创建多个实例。这和单例模式的初衷的不符的。为什么会造成这种情况呢?

想象这样一个场景,假设A线程已经运行到箭头所指的部分,此时判断语句已经结束,实例化new语句还没开始。B线程横刀夺爱,抢走了CPU使用权,同样也调用GetInstance函数。此时agvObject依然为null,因为A线程并没有运行new语句。那B线程就实例化了一个agvClass。

回到A线程,因为判断语句已经运行过了,所以不需要再次判断agvOject是否为空,也就错过了挽救的机会。直接运行new语句,再次实例化agvClass。这样系统就存在了两个agvClass。

2.线程单锁

确保当一个线程位于代码的临界区时,另外一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待,直到该对象被释放。

publiac static agvClass GetInstance() {lock();  //加锁if (agvObject == null) {agvObject = new agvClass();}unlock(); //解锁return agvObject;}

3.双重锁定

如果是单锁,每次调用GetInstance函数都需要lock,影响性能。因此出现了Double-Check Locking(双重锁定)。

publiac static agvClass GetInstance() {if (agvObject == null) {lock();  //加锁if (agvObject == null) {agvObject = new agvClass();}unlock(); //解锁}return agvObject;}

如果实例不为空,直接返回agvObject,不需要加锁解锁等步骤。只有在实例未被创建的时候再加锁处理。那为什么需要判断两次agvObject为空?

第一次判断是在普通情况下,防止多次加锁解锁影响性能而设定的。

第二判断是用于两次线程同时进入GetInstance情况设定的。假设A线程停留在如箭头所指的部分,此时new语句还未运行。和上面情况类似,B线程横刀夺爱,也调用了GetInstance,它首先会通过第一个if判断,因此此时实例尚未创建,但会停留在lock这里,排队等候。当A线程完成实例的创建,B继续运行,如果此时没有第二次判断,就会创建第二个实例,不符合单例模式。

懒汉模式和饿汉模式

1.懒汉模式

上面提到的模式就是懒汉模式,就是第一次用到类的示例才实例化。注意到,前面new语句放在GetInstance函数,这个函数需要外界调用才会发挥作用,然后new一个实例。形象点记忆,懒汉就是不到万不得已就不会去实例化类,我用不上那就先不管。懒汉模式是不安全的实现方式,是线程不安全的,需要加锁。

2.饿汉模式

类定义的时候就实例化,本身是线程安全,不需要加锁。饿汉,就是不管三七二十一,这个类编译的时候就实例化,不考虑后面用不用得上。用法如下所示,直接在类外实例化,即初始化即实例化。

public class agvClass{private static agvClass* agvObject;private agvClass(){}publiac static agvClass GetInstance() {return agvObject;}
}agvClass* agvClass::agvObject = new agvClass;

3.选择

懒汉:在访问量比较小,采用懒汉模式。到有需要的时间才实例化对象,那它就不会提前占据内存空间,代价就是后续每次访问都会判断是否为空,增加时间成本。这是以时间换空间。

饿汉:在访问量比较大,或者可能访问的线程比较多,采用饿汉模式。就算没用上实例对象,也会进行实例化,这是要占据一定内存的。但在后面需要使用的时候,就不需要判断之类语句,所以非常快速。这就是以空间换时间。

【C++】设计模式------单例模式(懒汉和饿汉)相关推荐

  1. 单例模式懒汉、饿汉和登记

    转载自  JAVA设计模式之单例模式 本文继续介绍23种设计模式系列之单例模式. 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记 ...

  2. java单例设计模式懒汉_Java设计模式之单例设计模式(懒汉、饿汉)

    [toc] Java设计模式之单例设计模式(懒汉.饿汉) 相信面试过的初中级Java开发的朋友可能都有遇到过单例设计模式的笔试题吧,如果之前没有背下来或者不理解,可以看看下面这篇文章,应该足够应付笔试 ...

  3. 【C++】单例模式(懒汉、饿汉)

    1.什么是单例模式? 顾名思义,单例模式就是在这个单例类确保只有一个对象被创建.也就是说这个类只能实例化一个对象. 特征:1.单例类最多只能有一个实例: 2.单例类必须自己创建自己唯一的实例: 3.单 ...

  4. java 单例模式 懒汉和饿汉

    单线程下的单例模式 定义: 确保一个类只有一个实例,并提供一个全局访问点. 单例代码: package wfb.pattern;public class Singleton {private stat ...

  5. 单例模式——懒汉与饿汉

    前言:设计模式是在不断编程中前人所总结的"兵书",将可能面对的大部分编程需求归纳总结为固定的模式,而单例模式则是最常见的设计模式之一. 设计模式 设计模式(Design Patte ...

  6. Java面试题 实现单例设计模式(懒汉、饿汉)

    懒汉: //懒汉,顾名思义比较懒,在用的时候才实例化 public class Singleton {//创建实例,注意,此时没有newprivate static volatile Singleto ...

  7. java 单例 饿汉式_Java-单例设计模式(懒汉与饿汉)

    单例设计模式 保证一个类在内存中只能有一个对象. 思路: 1)如果其他程序能够随意用 new 创建该类对象,那么就无法控制个数.因此,不让其他程序用 new 创建该类的对象. 2)既然不让其他程序 n ...

  8. [设计模式]单例模式(懒汉式,饿汉式)

    实现单例步骤: 1.构造函数私有化. 2.增加静态私有的当前类的指针变量. 3.提供静态对外接口,可以让用户获得单例对象. 单例 分为: 1.懒汉式 2.饿汉式 懒汉式 代码如下: class Sin ...

  9. Java多线程案例之单例模式(懒汉,饿汉,枚举)

    ⭐️前面的话⭐️ 本篇文章将介绍Java多线程中的几个典型案例之单例模式,所谓单例模式,就是一个类只有一个实例对象,本文将着重介绍在多线程的背景下,单例模式的简单实现.

  10. 【设计模式:单例模式】单例模式01:饿汉模式

    单例模式:饿汉模式 正文开始@Assassin 目录: 单例模式:饿汉模式 1. 什么是设计模式? 2. 单例设计模式: 2.1 什么是单例模式? 2.2 单例模式应用实例: 1. 什么是设计模式? ...

最新文章

  1. 细节:js 创建对象的几种模式举例
  2. mysql基础语句整理
  3. mysql 二进制日志
  4. QT的QAssociativeIterable类的使用
  5. java因子的计算方法是_JAVA分解质因子
  6. 最短哈密尔顿圈matlab解法_复杂制造过程最优哈密尔顿圈算法的MATLAB仿真与分析.doc...
  7. 「镁客早报」第九城市与法拉第未来成立合资公司,出资6亿美元;华为获得韩国运营商5G订单,占比95%... 1
  8. 2020最新文本检测算法TextFuseNet
  9. 联想A790E的root方法
  10. 金三银四:蚂蚁金服JAVA后端面试题及答案之二面
  11. UI自动化常见的等待方式
  12. 禅与摩托车维修艺术摘录
  13. Let_god_knows
  14. 360手机:360N5S Twrp、Root、Magisk教程
  15. 【数据处理】matlibplot绘图颜色对照表
  16. ECSHOP goods表字段分析
  17. oracle创建dba,oracle创建一个DBA用户
  18. 金立集团董事长-刘立荣-注意细节
  19. 买入开仓认购期权的风险有哪些 什么是买入平仓和买入开仓
  20. bash shell实现2048小游戏详解

热门文章

  1. 爬子第一篇:zol手机型号参数抓取
  2. 计算机中网络显示计算机5,电脑显示联接网络5,但无Internet访问,
  3. cucumber学习笔记 -- 测试报告
  4. 蓝牙耳机品牌排行榜前十名,2023年值得推荐的四款蓝牙耳机
  5. iOS开发键盘表情处理
  6. 盘点北邮IT类就业方向
  7. python解析html基础操作
  8. 妖人柴:网络赚钱,想倍增收入,你有绝活吗?
  9. Windows Developer Day - MSIX and Advanced Installer
  10. zookeeper常用命令 - 雨中散步撒哈拉