单例模式详细讲解

一.定义

        单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点

特点:

1.构造函数和析构函数私有化

2.禁用拷贝构造函数和赋值运算符重载(=delete)

3.利用静态成员函数和静态成员变量来给外界提供访问

二.恶汉式

       恶汉是非常霸道的,由此可见,对于恶汉式,我们程序加载时立即创建单例实例(无论是否需要)

代码如下:

//恶汉式:
class Singleton 
{
public://利用静态成员函数和静态成员变量来给外界提供访问static Singleton GetInstance(){return _instance;}	//禁用拷贝构造和赋值运算符Singleton(const Singleton&) =delete;Singleton& operator=(const Singleton&)=delete;
priavte://构造析构私有化:Singleton(){}~Singleton(){}static Singleton _instance;//定义一个对象 
};
//static类外实例化:
Singleton Singleton::_instance;

优点:

  • 线程安全(C++11保证静态变量的线程安全初始化)

  • 程序启动时就创建实例

  • 简单直接

缺点:     

  • 本质是通过空间换来的,可能导致空间浪费

三.懒汉式

        懒汉本质在于懒,说明只有当我们需要时才会创建单例对象,具有延迟实例化特点,通过调用GetInstance()函数来创建对象

优点;

按需创建对象,避免浪费空间

缺点:

基础实现是非线程安全的,需额外处理多线程问题

下面我们一一来讲解不同版本:

//懒汉式:
//初始版本--1
class Singleton
{
public:static Singleton* GetInstance(){if(nullptr==_instance){//创建对象_instance =new Singleton;}return _instance;}//禁用拷贝构造和赋值运算符Singleton(const Singleton&) =delete;Singleton& operator=(const Singleton&)=delete;
priavte://构造析构私有化:Singleton(){}~Singleton(){}static Singleton* _instance;//定义一个对象指针 
};
//类对象实例化: 
Singleton* Singleton::_instance=nullptr;

该版本问题如下:

该代码不是线程、进程安全的,具体是指如果多个线程同时调用到GetInstance中的if语句且都进入,就会new两个以上对象,无法确保只有一个类对象

解决方法:加锁

//懒汉式:
//初始版本--2
#include <mutex>
class Singleton
{
public:static Singleton* GetInstance(){//单检测法: _mutex.lock();if(nullptr==_instance){//创建对象_instance =new Singleton;}_mutex.unlock();return _instance;}//禁用拷贝构造和赋值运算符Singleton(const Singleton&) =delete;Singleton& operator=(const Singleton&)=delete;
priavte://构造析构私有化:Singleton(){}~Singleton(){}static Singleton* _instance;//定义一个对象指针static std::mutex _mutex; 
};
//类对象实例化: 
Singleton* Singleton::_instance=nullptr;
std::mutex Singleton::_mutex;

上面我们利用C++中提供的锁解决了多线程问题,但是如果每次访问都要加锁,并且多线程访问只有一个能够进去,其他要等待,性能非常不好

下面我们来利用双检测法来解决问题:

//懒汉式:
//初始版本--3
#include <mutex>
class Singleton
{
public:static Singleton* GetInstance(){//双检测法: if(nullptr==_instance){_mutex.lock();if(nullptr==_instance){//创建对象 _instance =new Singleton;}_mutex.unlock();}return _instance;}//禁用拷贝构造和赋值运算符Singleton(const Singleton&) =delete;Singleton& operator=(const Singleton&)=delete;
priavte://构造析构私有化:Singleton(){}~Singleton(){}static Singleton* _instance;//定义一个对象指针static std::mutex _mutex; 
};
//类对象实例化: 
Singleton* Singleton::_instance=nullptr;
std::mutex Singleton::_mutex;

该双检测法并非是正确的双检测法,原因:如果CPU执行new的指令发生问题,即如果先返回对象指针,这样就会接受到一个nullptr的指针,出现问题

newCPU执行过程;

1.分配空间 malloc

2.调用构造函数 (类)

3.返回对象指针

下面我们来学习正确的双检测法;

//懒汉式:
//初始版本--4
#include <mutex>
#include <atmoic>
class Singleton
{
public:static Singleton* GetInstance(){//双检测法: (正确写法)Singleton* tmp = _instance.load(std::memory_order_relaxed);//std::memory_order_relaxed---C++11 引入,最宽松的内存顺序约束,仅保证原子性,不提供线程间的同步或顺序保证std::atomic_thread_fence(std::memory_order_acquire); if(nullptr==_instance){_mutex.lock();tmp = _instance.load(std::memory_order_relaxed);if (tmp == nullptr) {tmp = new Singleton();std::atomic_thread_fence(std::memory_order_release);_instance.store(tmp, std::memory_order_relaxed);}_mutex.unlock();}return tmp;}//禁用拷贝构造和赋值运算符Singleton(const Singleton&) =delete;Singleton& operator=(const Singleton&)=delete;
priavte://构造析构私有化:Singleton(){}~Singleton(){}static std::atomic<Singleton*> _instance;;//定义一个对象指针static std::mutex _mutex; 
};
//类对象实例化: 
std::atomic<Singleton*> Singleton::_instance(nullptr);
std::mutex Singleton::_mutex;

利用Atomic来保证原子性,保证双检测不会受到CPU执行指令顺序影响

优点:

线程安全,高性能(只有第一次需要加锁)

缺点:

实现复杂,需要注意内存屏障

最后,我们来学习发明单例模式的作者是如何写的:

//作者实现的: 
class Singleton 
{
public:static Singleton& getInstance() {static Singleton instance;return instance;}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private:Singleton() {}~Singleton() {}
};

特点;

线程安全(C++11保证局部静态变量的线程安全初始化)

延迟初始化

简洁高效

不需要考虑内存释放问题

只能说不愧是大佬!!!)

其实我们也可以考虑下智能指针和call_once来实现,大家可以试试

最后,感谢你的浏览,点个关注吧!!!

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

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

相关文章

KORGym:评估大语言模型推理能力的动态游戏平台

KORGym&#xff1a;评估大语言模型推理能力的动态游戏平台 现有评估基准多受领域限制或 pretraining 数据影响&#xff0c;难以精准测LLMs内在推理能力。KORGym平台应运而生&#xff0c;含50余款游戏&#xff0c;多维度评估&#xff0c;本文将深入解析其设计、框架、实验及发现…

ISPDiffuser文章翻译理解

ISPDiffuser: Learning RAW-to-sRGB Mappings with Texture-Aware Diffusion Models and Histogram-Guided Color Consistency翻译 Type: Conference paper Author: Yang Ren1,4, Hai Jiang1,4, Menglong Yang1,2,†, Wei Li1,2, Shuaicheng Liu3,4,† Select: ⭐️⭐️⭐️⭐…

C++线程池执行步骤分析,总结线程池流程

线程池流程总结&#xff1a;1、构造函数中创建线程&#xff0c;并添加到线程池&#xff08;构造函数返回时&#xff0c;线程自动启动&#xff0c;并停在等待wait&#xff1a;从线程池取出一个任务处&#xff09;&#xff1b; 2、主线程中添加任务&#xff0c;到任务队列。并用“…

Java 通过 HttpURLConnection发送 http 请求

问题&#xff1a; 在调试 kill 接口的时候&#xff0c;对方的服务用的是 Django RestFramework 框架提供的接口&#xff0c;用 python 请求时得到的内容如下&#xff1a; ➜ ~ python3 test.py <Response [200]> "true" // 对应的代码是 print(response, r…

【PTA数据结构 | C语言版】列出连通集

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 给定一个有 n 个顶点和 m 条边的无向图&#xff0c;请用深度优先遍历&#xff08;DFS&#xff09;和广度优先遍历&#xff08;BFS&#xff09;分别列出其所有的连通集。假设顶点从 0 到 n−1 编号。…

GoLang教程005:switch分支

3.4 Switch分支 在 GoLand&#xff08;其实是 JetBrains 开发的 Go 编程语言 IDE&#xff09;中&#xff0c;switch 是 Go 语言&#xff08;Golang&#xff09; 的一个重要控制结构&#xff0c;用于替代多个 if-else 语句。 ✅ 特点说明特性说明自动 breakGo 的 switch 语句默认…

uniapp相关地图 API调用

目录 一、 注意事项&#xff1a; manifest.json需增加配置 二、获取用户收货地址 [uni.chooseAddress] 三、获取当前的地理位置、速度 [uni.getLocation] 四、打开地图选择位置、查看位置(导航) [uni.chooseLocation] [uni.openLocation] 五、使用腾讯地图逆地址解析接口实…

Java学习----NIO模型

在 Java 的 I/O 模型中&#xff0c;NIO&#xff08;Non - Blocking I/O&#xff0c;非阻塞 I/O&#xff09;是对 BIO 的重要改进。它为高并发场景提供了更高效的处理方式&#xff0c;在众多 Java 应用中发挥着关键作用。NIO模型的核心在于非阻塞和多路复用&#xff0c;其采用 “…

MySQL计数函数count原理分析

前言 统计表中数据的条数是非常常用的操作,但是咱们常用的InnoDB存储引擎计数函数是现时统计的,所以会出现性能的问题,这次我准备分享计数函数count的原理,保证之后遇到计数方面的问题都可以轻易灵活的解决 与MyISAM存储引擎相比,MyISAM存储引擎是自己记录了表中数据的条数,但…

Day07_网络编程20250721_大项目

基本代码&#xff1a;搭建服务器客户端&#xff0c;要求服务器使用 epoll 模型客户端使用多线程服务器打开数据库&#xff0c;表单格式如下name text primary key pswd text not null客户端做一个简单的界面&#xff1a;1&#xff1a;注册2&#xff1a;登录无论注册还是登录&am…

20250721

P5357 【模板】AC 自动机 - 洛谷 主要是构建fail树 /* 我们可以知道的是&#xff0c;当访问一个点x时&#xff0c;接下来需要跳转其fail[x]&#xff0c;以此类推&#xff0c;如果在某个fail[x]上出现了一个字符串&#xff0c;那么相应的统计次数应该加1&#xff0c;然后当访…

【INT四则优先算式】2022-9-22

缘由ccf201903-2二十四点我用暴力破解做的&#xff0c;但是两个程序一个拿到了满分&#xff0c;一个拿到了50分&#xff0c;看了很长时间也没看出问题在哪里&#xff0c;希望有英雄慧眼帮我看一下-编程语言-CSDN问答 void INT四则优先算式() {//缘由https://ask.csdn.net/ques…

本地k8s集群的搭建

windows机器&#xff0c;考虑如果使用云服务器&#xff0c;每年的开销还是太大&#xff0c;不值得&#xff0c;自己只是做demo&#xff0c;了解各种配置和使用即可&#xff0c;使用VMware的虚拟机来搭建k8s集群 使用docker安装rancher和k8s yum -y install chronycat > /et…

B树、B+树的区别及MySQL为何选择B+树

B树与B树 B树和B树都是自平衡的多路搜索树&#xff0c;广泛应用于数据库和文件系统中&#xff0c;用于高效管理大量数据。它们的设计目标是在磁盘存储环境下减少I/O操作次数&#xff0c;提高数据访问效率。下面我将逐步解释两者的定义、特性、比较以及应用场景&#xff0c;确保…

Unity之可视化编程VisualScripting快速入门

文章目录 前言 脚本机和状态机 脚本图ScriptGraph 脚本图 子图 自定义事件 状态图StateGraph 状态图 Start状态 创建新状态 过渡连接 常用功能 射线检测 补间动画 按钮点击 前言 可视化脚本使您无需编写代码即可为游戏或应用程序创建逻辑。可视化脚本使用基于节点的可视化图形…

2025三掌柜赠书活动第二十五期 网络安全应急响应实战

目录 前言 网络安全的重要性 关于《网络安全应急响应实战》 编辑推荐 内容简介 作者简介 图书目录 《网络安全应急响应实战》全书速览 结束语 前言 在当今数字化时代&#xff0c;网络安全已经成为企业和个人都无法忽视的重要问题。随着网络技术的飞速发展&#xff0c;…

车载软件架构 --- 软件开发面临的问题

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…

MySQL 8.0 OCP 1Z0-908 题目解析(31)

题目121 Choose two. Examine this command, which executes successfully on InnoDB Cluster: dba.dropMetadataSchema() Which two statements are true? □ A) The mysql_innodb_cluster_metadata schema is dropped from the instance where the connection was establish…

本地生活服务 app 同城信息发布系统搭建

一、逻辑分析用户需求层面&#xff1a;对于发布者来说&#xff0c;需要一个便捷的界面来输入同城信息&#xff0c;包括但不限于房屋租售、招聘求职、二手交易、活动推广等各类信息。发布者要能够上传相关图片、详细描述信息内容、设置价格&#xff08;如果有需要&#xff09;、…

[Python] -项目实战4- 利用Python进行Excel批量处理

一、为什么要批量处理Excel文件? 节省时间:人工对数十、数百个 Excel 文件重复操作不现实,Python 批量处理一次搞定。 保证一致性:统一格式、统一操作,避免手动误差。 易于集成:可嵌入日常自动化流程,支持定时和触发执行。 二、常用库及选型建议 库 作用 优势 局限 p…