创建型模式 | 单例模式
一、单例模式
单例模式(Singleton Pattern),使用最广泛的设计模式之一。其意图是保证一个类仅有一个实例被构造,并提供一个访问它的全局访问接口,该实例被程序的所有模块共享。
1、饿汉式
1.1、基础版本
在程序启动后立刻构造单例,饿汉式实现一个单例类步骤如下:
- 定义一个单例类
- 私有化构造函数,防止外界直接创建单例类的对象
- 禁用拷贝构造,移动赋值等函数,可以私有化,也可以直接使用=delete
- 使用一个公有的静态方法获取该实例
- 确保在第一次调用之前该实例被构造
代码实现
#include <iostream>
#include <string>
using namespace std;// 单例类
class Singleton {
protected:Singleton() { std::cout << "Singleton: call Constructor\n"; };static Singleton *m_pInst;public:Singleton(const Singleton &) = delete;Singleton &operator=(const Singleton &) = delete;virtual ~Singleton() { std::cout << "Singleton: call Destructor\n"; }static Singleton* GetInstance() {return m_pInst;}
};Singleton *Singleton::m_pInst = new Singleton;int main()
{Singleton *pInst1 = Singleton::GetInstance();Singleton *pInst2 = Singleton::GetInstance();cout << "pInst1 : " << pInst1 << endl;cout << "pInst2 : " << pInst2 << endl;return 0;
}
输出结果
Singleton: call Constructor
pInst1 : 0xf71760
pInst2 : 0xf71760Process returned 0 (0x0)   execution time : 0.203 s
Press any key to continue.
从输出结果可以看出来,在执行
main函数之前,单例类对象已经被创建出来。获取实例的函数也不需要进行判空操作,因此也就不用双重检测锁来保证线程安全了,它本身已经是线程安全状态了,但是内存泄漏的问题还是要解决的。
1.2、基于资源管理的饿汉实现
内存泄漏解决方法有两个:智能指针&静态嵌套类。
1.2.1、智能指针解决方案
将实例指针更换为智能指针,另外智能指针在初始化时,还需要添加公有的销毁函数,因为析构函数私有化了。
#include <iostream>
#include <string>
#include <mutex>
#include <memory>
#include <thread>
using namespace std;// 单例类
class Singleton {
protected:Singleton() { std::cout << "Singleton: call Constructor\n"; };static shared_ptr<Singleton> instance;private:Singleton(const Singleton &) = delete;Singleton &operator=(const Singleton &) = delete;virtual ~Singleton() { std::cout << "Singleton: call Destructor\n"; }public:// 自定义销毁实例方法static void DestoryInstance(Singleton* x) {delete x;}static shared_ptr<Singleton> GetInstance() {return instance;}
};// 初始化
shared_ptr<Singleton> Singleton::instance(new Singleton(), DestoryInstance);int main()
{cout << "main开始" << endl;thread t1([] {shared_ptr<Singleton> s1 = Singleton::GetInstance();});thread t2([] {shared_ptr<Singleton> s2 = Singleton::GetInstance();});t1.join();t2.join();cout << "main结束" << endl;return 0;
}输出结果
Singleton: call Constructor
main开始
main结束
Singleton: call DestructorProcess returned 0 (0x0)   execution time : 0.116 s
Press any key to continue.
从输出结果可以看出来实例内存在程序运行结束后被正常释放。
1.2.2、静态嵌套类解决方案
类中定义一个嵌套类,初始化该类的静态对象,当程序结束时,该对象进行析构的同时,将单例实例也删除了。
#include <iostream>
#include <string>
#include <mutex>
#include <memory>
#include <thread>
using namespace std;// 单例类
class Singleton {// 定义一个删除器(嵌套类)class Deleter {public:Deleter() {};~Deleter() {if (m_pInst != nullptr) {cout << "删除器启动" << endl;delete m_pInst;m_pInst = nullptr;}}};protected:Singleton() { std::cout << "Singleton: call Constructor\n"; };static Deleter m_deleter;static Singleton* m_pInst;private:Singleton(const Singleton &) = delete;Singleton &operator=(const Singleton &) = delete;virtual ~Singleton() { std::cout << "Singleton: call Destructor\n"; }public:static Singleton* GetInstance() {return m_pInst;}
};Singleton *Singleton::m_pInst = new Singleton;
Singleton::Deleter Singleton::m_deleter;int main()
{cout << "main开始" << endl;thread t1([] {Singleton *pInst1 = Singleton::GetInstance();});thread t2([] {Singleton *pInst2 = Singleton::GetInstance();});t1.join();t2.join();cout << "main结束" << endl;return 0;
}输出结果
Singleton: call Constructor
main开始
main结束
删除器启动
Singleton: call DestructorProcess returned 0 (0x0)   execution time : 0.254 s
Press any key to continue.
从输出结果可以看出来单例类对象在程序运行结束时正常被释放。
2、懒汉式
2.1、基础版本
在使用类对象(单例实例)时才会去创建,实现如下:
#include <iostream>
#include <string>
#include <mutex>
#include <memory>
#include <thread>
using namespace std;// 单例类
class Singleton
{
public:static Singleton* GetInstance() {if (m_pInst == nullptr) {m_pInst = new Singleton;}return m_pInst;}private:// 私有构造函数Singleton() { cout << "构造函数启动。" << endl; };// 私有析构函数~Singleton() { cout << "析构函数启动。" << endl; };private:static Singleton* m_pInst;
};// 初始化
Singleton* Singleton::m_pInst = nullptr;int main()
{cout << "main开始" << endl;thread t1([] {Singleton *pInst1 = Singleton::GetInstance();});thread t2([] {Singleton *pInst2 = Singleton::GetInstance();});t1.join();t2.join();cout << "main结束" << endl;return 0;
}上面的懒汉式存在两方面问题,一是:多线程场景存在并发问题;二是:创建的单例对象在使用完成后不会被释放存在资源泄露问题。
2.2、双重检查
使用双重检查解决多线程并发问题,核心代码如下:
static Singleton* GetInstance() {if (m_pInst == nullptr) {// 双重检查lock_guard<mutex> l(m_mutex);if (m_pInst == nullptr) {m_pInst = new Singleton();}}return m_pInst;
}
双重检查能解决多线程并发问题,同时效率也比单检查要高,调用
GetInstance时只有当单例对象没有被创建时才会加锁,下面是单检查的实现,通过对比即可发现双检查的优点,如下:
static Singleton* GetInstance() {lock_guard<mutex> l(m_mutex);if (m_pInst == nullptr) {m_pInst = new Singleton();}return m_pInst;
}
2.3、基于静态局部对象的实现
C++11后,规定了局部静态对象在多线程场景下的初始化行为,只有在首次访问时才会创建实例,后续不再创建而是获取。若未创建成功,其他的线程在进行到这步时会自动等待。注意:C++11前的版本不是这样的。因为有上述的改动,所以出现了一种更简洁方便优雅的实现方法,基于局部静态对象实现,如下:
#include <iostream>
#include <string>
#include <mutex>
#include <memory>
#include <thread>
using namespace std;// 单例类
class Singleton
{
public:static Singleton* GetInstance() {static Singleton instance;return &instance;}private:// 私有构造函数Singleton() { cout << "构造函数启动。" << endl; };// 私有析构函数~Singleton() { cout << "析构函数启动。" << endl; };
};int main()
{cout << "main开始" << endl;thread t1([] {Singleton *pInst1 = Singleton::GetInstance();});thread t2([] {Singleton *pInst2 = Singleton::GetInstance();});t1.join();t2.join();cout << "main结束" << endl;return 0;
}相关文章:
创建型模式 | 单例模式
一、单例模式 单例模式(Singleton Pattern),使用最广泛的设计模式之一。其意图是保证一个类仅有一个实例被构造,并提供一个访问它的全局访问接口,该实例被程序的所有模块共享。 1、饿汉式 1.1、基础版本 在程序启动后立刻构造单例࿰…...
【无标题】欢迎使用Markdown编辑器
这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…...
 
Postgresql中PL/pgSQL的游标、自定义函数、存储过程的使用
场景 Postgresql中PL/pgSQL代码块的语法与使用-声明与赋值、IF语句、CASE语句、循环语句: Postgresql中PL/pgSQL代码块的语法与使用-声明与赋值、IF语句、CASE语句、循环语句-CSDN博客 上面讲了基本语法,下面记录游标、自定义函数、存储过程的使用。 …...
 
【IDEA】Intellij IDEA相关配置
IDEA 全称 IntelliJ IDEA,是java编程语言的集成开发环境。IntelliJ在业界被公认为最好的Java开发工具,尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以说是超…...
 
GD32移植STM32工程(因为懒,所以移植)
文章目录 一、前言二、差异性三、软件移植部分1.前期准备1.1 安装GD32固件库1.2 选择所用芯片 2.修改程序2.1 启动时间(内部时钟可不改)2.2 主频2.2.1 系统时钟配置2.2.2 108MHz宏定义第一处第二处第三处第四处第五处 2.2.3 串口2.2.4 FLASH 四、总结 一…...
 
mt5和mt4交易软件有什么区别?
MetaTrader 4(MT4)和MetaTrader 5(MT5)是两种广泛使用的外汇和金融市场交易平台,由MetaQuotes公司开发。尽管它们都是外汇交易的常见选择,但在功能和特性上存在一些区别。以下是MT4和MT5之间的主要区别&…...
 
零刻EQ12 N100 双2.5G网口 All In One新手教程
零刻EQ12 N100 双2.5G网口 All In One新手教程 前言1.硬件配置2.准备工作2.1. ESXI8.0U2镜像2.2. Rufus磁盘工具下载2.3. ikuai镜像下载2.4. StarWindConverter虚拟磁盘格式转换工具下载2.5. OpenWrt镜像下载2.6. 黑群晖RR引导镜像下载(DSM7.2)2.7. 需要准备的硬件2.8. 格式化需…...
 
竞赛保研 基于Django与深度学习的股票预测系统
文章目录 0 前言1 课题背景2 实现效果3 Django框架4 数据整理5 模型准备和训练6 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 **基于Django与深度学习的股票预测系统 ** 该项目较为新颖,适合作为竞赛课题方向ÿ…...
 
听GPT 讲Rust源代码--src/tools(16)
File: rust/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs rust-analyzer是一个基于Rust语言的IntelliSense引擎,用于提供IDE自动补全、代码导航和其他代码编辑功能。在rust-analyzer的源代码中,rust/src/tools/rust-analyzer…...
Leetcoed 双指针
三数之和 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。 请你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组…...
 
关于“Python”的核心知识点整理大全31
目录 12.4.2 在屏幕上绘制飞船 alien_invasion.py 编辑12.5 重构:模块 game_functions 12.5.1 函数 check_events() game_functions.py alien_invasion.py 12.5.2 函数 update_screen() game_functions.py alien_invasion.py 12.6 驾驶飞船 12.6.1 响应…...
 
第1章 SpringBoot开发入门
学习目标 了解SpringBoot的优点 掌握SpringBoot项目的构建 掌握SpringBoot的单元测试和热部署 熟悉SpringBoot的自动化配置原理 熟悉SpringBoot的执行流程 随着互联网的兴起,Spring势如破竹地占据了Java领域轻量级开发的王者之位。随着Java语言的发展以及市场…...
 
利用prometheus+grafana进行Linux主机监控
文章目录 一.架构说明与资源准备二.部署prometheus1.上传软件包2.解压软件包并移动到指定位置3.修改配置文件4.编写启动脚本5.启动prometheus服务 三.部署node-exporter1.上传和解压软件包2.设置systemctl启动3.启动服务 四.部署grafana1.安装和启动grafana2.设置prometheus数据…...
单词反转(字符串)
题目名字 单词反转 题目链接 题意 输入倒序的字符串,要求输出正序的字符串 思路 用while输入,这样当出现输入是空格时自动划分上一个为一个单词然后再次反输出 while循环的条件是当不再输入的时候,因为是字符串,不用getline输入…...
 
【Java 集合】LinkedBlockingDeque
在开始介绍 LinkedBlockingDeque 之前, 我们先看一下 LinkedBlockingDeque 的类图: 从其中可以看出他直接实现了 BlockingDeque 接口, 而 BlockingDeque 又实现了 BlockingQueue 的接口, 所以它本身具备了队列的特性。 而实现 BlockingDeque 使其在 BlockingQueue 的基础上多了…...
 
【hacker送书第3期】OpenCV轻松入门:面向Python(第2版)
第3期图书推荐 内容简介作者简介图书目录专家推荐参与方式 内容简介 本书基于面向 Python 的 OpenCV(OpenCV for Python),介绍了图像处理的方方面面。本书以 OpenCV 官方文档的知识脉络为主线,并对细节进行补充和说明。书中不仅介绍了 OpenCV 函数的使用…...
 
手把手教你isPalindrome 方法在密码验证中的应用
在信息安全领域中,密码验证是一个极为重要的组成部分。一个强密码应具备足够的复杂性,以免遭到破解。而回文密码是一种具备特殊性质的密码,其正序和倒序相同,因此具有极高的安全性,并能发挥重要作用。在实际密码策略中…...
 
drf入门规范(二)
四 RESTful API规范 REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征性状态转移)。 它首次出现在2000年Roy Fielding的博士论文中。 RESTful是一种定义Web API接口的设计风格,尤其适用…...
使用Redis和Nginx分别实现限制接口请求频率
前言 为啥需要限制接口请求频率?这个是因为防止接口一直被刷,比如发送手机验证码的接口,一直被刷的话,费钱费资源的,至少做点基本的防护工作。以下分别使用Redis和Nginx实现限制接口请求频率方案。 一、基于Redis实现…...
 
ansible模块 (7-13)
模块 7、hostname模块: 远程主机名管理模块 ansible 192.168.10.202 -m hostname -a nameliu 8、copy模块: 用于复制指定的主机文件到远程主机的模块 常用参数: dest: 指出要复制的文件在哪,必须使用绝对路径。如果源目标是…...
 
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
 
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
 
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
 
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
 
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
