当前位置: 首页 > news >正文

【11】c++设计模式——>单例模式

单例模式是什么

在一个项目中,全局范围内,某个类的实例有且仅有一个(只能new一次),通过这个唯一的实例向其他模块提供数据的全局访问,这种模式就叫单例模式。单例模式的典型应用就是任务队列。

为什么要使用单例模式

单例模式充当的就是一个全局变量,为什么不直接使用全局变量呢,因为全局变量破坏类的封装,而且不受保护,访问不受限制。

在这里插入图片描述

饿汉模式

饿汉模式是在类加载时进行实例化的。

#include<iostream>
using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance()  //获取单例的方法{cout << "我是一个饿汉模式单例" << endl;return m_taskQ;}private:TaskQueue() = default; //无参构造static TaskQueue* m_taskQ; //静态成员需要在类外定义
};
TaskQueue* TaskQueue::m_taskQ = new TaskQueue;//new一个实例;int main()
{TaskQueue* obj = TaskQueue::getInstance();
}

懒汉模式

懒汉模式是在需要使用的时候再进行实例化

#include<iostream>
using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance()  //获取单例的方法{if (nullptr == m_taskQ){m_taskQ = new TaskQueue;}return m_taskQ;}private:TaskQueue() = default; //无参构造static TaskQueue* m_taskQ; //静态成员需要在类外定义
};
TaskQueue* TaskQueue::m_taskQ = nullptr;int main()
{TaskQueue* obj = TaskQueue::getInstance();
}

在调用**getInstance()**函数获取单例对象的时候,如果在单线程情况下是没有什么问题的,如果是多个线程,调用这个函数去访问单例对象就有问题了。假设有三个线程同时执行了getInstance()函数,在这个函数内部每个线程都会new出一个实例对象。此时,这个任务队列类的实例对象不是一个而是3个,很显然这与单例模式的定义是相悖的。

线程安全问题

对于饿汉模式来说是没有线程安全问题的,在这种模式下访问单例对象时,这个对象已经被创建出来了,要解决懒汉模式的线程安全问题,最常用的解决方案就是使用互斥锁,可以将创建单例对象的代码使用互斥锁锁住:

#include<iostream>
#include<mutex>
using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance()  //获取单例的方法{m_mutex.lock();if (nullptr == m_taskQ){cout << "我加了互斥锁" << endl;m_taskQ = new TaskQueue;}m_mutex.unlock();return m_taskQ;}private:TaskQueue() = default; //无参构造static TaskQueue* m_taskQ; //静态成员需要在类外定义static mutex m_mutex; //定义为静态的,因为静态函数只能使用静态变量
};
mutex TaskQueue::m_mutex;
TaskQueue* TaskQueue::m_taskQ = nullptr;int main()
{TaskQueue* obj = TaskQueue::getInstance();
}

在上面代码的10~13 行这个代码块被互斥锁锁住了,也就意味着不论有多少个线程,同时执行这个代码块的线程只能是一个(相当于是严重限行了,在重负载情况下,可能导致响应缓慢)。我们可以将代码再优化一下:

#include<iostream>
#include<mutex>
using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance()  //获取单例的方法{if (nullptr == m_taskQ){m_mutex.lock();if (nullptr == m_taskQ){cout << "我加了互斥锁" << endl;m_taskQ = new TaskQueue;}m_mutex.unlock();}return m_taskQ;}private:TaskQueue() = default; //无参构造static TaskQueue* m_taskQ; //静态成员需要在类外定义static mutex m_mutex;
};
mutex TaskQueue::m_mutex;
TaskQueue* TaskQueue::m_taskQ = nullptr;int main()
{TaskQueue* obj = TaskQueue::getInstance();
}

双重检查锁定问题

#include<iostream>
#include<mutex>
#include<atomic>
using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance()  //获取单例的方法{TaskQueue* taskQ = m_taskQ.load(); //取出来单例的值if (nullptr == taskQ){m_mutex.lock();taskQ = m_taskQ.load();if (nullptr == taskQ){cout << "我加了原子" << endl;taskQ = new TaskQueue;m_taskQ.store(taskQ); //保存到原子变量中}m_mutex.unlock();}return m_taskQ.load();}private:TaskQueue() = default; //无参构造static atomic<TaskQueue*>m_taskQ; //定义为原子变量static mutex m_mutex;
};
mutex TaskQueue::m_mutex;
atomic<TaskQueue*> TaskQueue::m_taskQ;int main()
{TaskQueue* obj = TaskQueue::getInstance();
}

对于m_taskQ = new TaskQueue这行代码来说,我们期望的执行机器指令执行顺序是:
(1)分配用来保存TaskQueue对象的内存;
(2)在分配好的内存中构造一个TaskQueue对象(即初始化内存);
(3)使用m_taskQ指针指向分配的内存。
但是对多线程来说,机器指令可能会被重新排列;即:
(1)分配内存用于保存 TaskQueue 对象。
(2)使用 m_taskQ 指针指向分配的内存。
(3)在分配的内存中构造一个 TaskQueue 对象(初始化内存)。
这样重排序并不影响单线程的执行结果,但是在多线程中就会出问题。如果线程A按照第二种顺序执行机器指令,执行完前两步之后失去CPU时间片被挂起了,此时线程B在第3行处进行指针判断的时候m_taskQ 指针是不为空的,但这个指针指向的内存却没有被初始化,最后线程 B 使用了一个没有被初始化的队列对象就出问题了(出现这种情况是概率问题,需要反复的大量测试问题才可能会出现)。
在C++11中引入了原子变量atomic,通过原子变量可以实现一种更安全的懒汉模式的单例,代码如下:

#include<iostream>
#include<mutex>
#include<atomic>
using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance()  //获取单例的方法{TaskQueue* taskQ = m_taskQ.load(); //取出来单例的值if (nullptr == taskQ){m_mutex.lock();taskQ = m_taskQ.load();if (nullptr == taskQ){cout << "我加了原子" << endl;taskQ = new TaskQueue;m_taskQ.store(taskQ); //保存到原子变量中}m_mutex.unlock();}return m_taskQ.load();}private:TaskQueue() = default; //无参构造static atomic<TaskQueue*>m_taskQ; //定义为原子变量static mutex m_mutex;
};
mutex TaskQueue::m_mutex;
atomic<TaskQueue*> TaskQueue::m_taskQ;int main()
{TaskQueue* obj = TaskQueue::getInstance();
}

使用局部静态

c++11新特性有如下规定:如果指令逻辑进入一个未被初始化的声明标量,所有并发执行应当等待该变量完成初始化。

#include<iostream>using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance()  //获取单例的方法{static TaskQueue m_taskQ; //未被初始化return &m_taskQ;}void print(){cout << "hello, world!!!" << endl;}private:TaskQueue() = default; //无参构造
};int main()
{TaskQueue* obj = TaskQueue::getInstance();obj->print();
}

饿汉模式和懒汉模式的区别

懒汉模式的缺点是在创建实例对象的时候有安全问题,但这样可以减少内存的浪费(如果用不到就不去申请内存了)。饿汉模式则相反,在我们不需要这个实例对象的时候,它已经被创建出来,占用了一块内存。对于现在的计算机而言,内存容量都是足够大的,这个缺陷可以被无视。

相关文章:

【11】c++设计模式——>单例模式

单例模式是什么 在一个项目中&#xff0c;全局范围内&#xff0c;某个类的实例有且仅有一个&#xff08;只能new一次&#xff09;&#xff0c;通过这个唯一的实例向其他模块提供数据的全局访问&#xff0c;这种模式就叫单例模式。单例模式的典型应用就是任务队列。 为什么要使…...

深度学习-卷积神经网络-AlexNET

文章目录 前言1.不同卷积神经网络模型的精度2.不同神经网络概述3.卷积神经网络-单通道4.卷积神经网络-多通道5.池化层6.全连接层7.网络架构8.Relu激活函数9.双GPU10.单GPU模型 1.LeNet-52.AlexNet1.架构2.局部响应归一化&#xff08;VGG中取消了&#xff09;3.重叠/不重叠池化4…...

人机关系不是物理关系也不是数理关系

人机关系是一种复杂的社会技术系统&#xff0c;涉及到人类和机器、环境之间的相互作用和影响。它不仅限于物理接触和数理规律&#xff0c;同时还包括了思维、情感、意愿等方面的交流和互动。在人机关系中&#xff0c;人类作为使用者和机器作为工具&#xff08;将来可能会上升到…...

<html dir=ltr>是什么意思?

<html dirltr>的意思是&#xff1a; 文字默认从左到右排列 说明&#xff1a; HTML--超级文本标记语言 dir 属性 -- (文字的)排列方式属性 取值&#xff1a; ltr -- 代表左到右的排列方式 rtl -- 代表右到左的排列方式 默认值:ltr 示例&#xff1a; ltr左到右的对…...

工厂模式:简化对象创建的设计思想 (设计模式 四)

引言 在软件开发中&#xff0c;我们经常需要创建各种对象实例来满足不同的需求。通常情况下&#xff0c;我们会使用new关键字直接实例化对象&#xff0c;但这种方法存在一些问题&#xff0c;比如对象的创建逻辑分散在代码中&#xff0c;难以维护和扩展&#xff0c;同时也违反了…...

【2023最新】微信小程序中微信授权登录功能和退出登录功能实现讲解

文章目录 一、讲解视频二、小程序前端代码三、后端Java代码四、备注 一、讲解视频 教学视频地址&#xff1a; 视频地址 二、小程序前端代码 // pages/profile/profile.js import api from "../../utils/api"; import { myRequest } from "../../utils/reques…...

复习 --- C++运算符重载

.5 运算符重载 运算符重载概念&#xff1a;对已有的运算符重新进行定义&#xff0c;赋予其另外一种功能&#xff0c;以适应不同的数据类型 4.5.1 加号运算符重载 作用&#xff1a;实现两个自定义数据类型相加的运算 1 #include<iostream>2 using namespace std;3 /…...

复习 --- select并发服务器

selectIO多路复用并发服务器&#xff0c;是通过轮询检测文件描述符来实现并发 将内核要检测文件描述符放入集合中&#xff0c;调用select函数&#xff0c;通知内核区检测文件描述符集合中的文件描述符是否准备就绪&#xff0c;即对应的空间中是否有数据 对准备就绪的文件描述…...

程序三高的方法

程序三高的方法 目录概述需求&#xff1a; 设计思路实现思路分析1.1&#xff09;高并发 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait for change,c…...

全志ARM926 Melis2.0系统的开发指引⑦

全志ARM926 Melis2.0系统的开发指引⑦ 编写目的11. 调屏11.1. 调屏步骤简介11.1.1. 判断屏接口。11.1.2. 确定硬件连接。11.1.3. 配置显示部分 sys_config.fex11.1.3.1. 配置屏相关 IO 11.1.4. Lcd_panel_cfg.c 初始化文件中配置屏参数11.1.4.1. LCD_cfg_panel_info11.1.4.2. L…...

全志ARM926 Melis2.0系统的开发指引⑧

全志ARM926 Melis2.0系统的开发指引⑧ 编写目的12.5. 应用程序编写12.5.1. 简单应用编写12.5.1.1. 注册应用12.5.1.2. 创建管理窗口12.5.1.3. 实现管理窗口消息处理回调函数12.5.1.4. 创建图层12.5.1.5. 创建 framewin12.5.1.6. 实现 framewin 消息处理回调函数 -. 全志相关工具…...

区别对比表:阿里云轻量服务器和云服务器ECS对照表

阿里云轻量应用服务器和云服务器ECS区别对照表&#xff0c;一看就懂的适用人群、使用场景、优缺点、使用限制、计费方式、网路和镜像系统全方位对比&#xff0c;阿里云服务器网分享ECS和轻量应用服务器区别对照表&#xff1a; 目录 轻量应用服务器和云服务器ECS区别对照表 轻…...

【做题笔记】多项式/FFT/NTT

HDU1402 - A * B Problem Plus 题目链接 大数乘法是多项式的基础应用&#xff0c;其原理是将多项式 f ( x ) a 0 a 1 x a 2 x 2 a 3 x 3 ⋯ a n x n f(x)a_0a_1xa_2x^2a_3x^3\cdotsa_nx^n f(x)a0​a1​xa2​x2a3​x3⋯an​xn中的 x 10 x10 x10&#xff0c;然后让大数的…...

网课搜题 小猿题库多接口微信小程序源码 自带流量主

多接口小猿题库等综合网课搜题微信小程序源码带流量主&#xff0c;网课搜题小程序, 可以开通流量主赚钱 搭建教程1, 微信公众平台注册自己的小程序2, 下载微信开发者工具和小程序的源码3, 上传代码到自己的小程序 源码下载&#xff1a;https://download.csdn.net/download/m0_…...

centos安装conda python3.10

最新版本的conda自带python3.10,直接安装即可。 手动创建一个conda文件夹&#xff0c;进入该文件夹&#xff0c;然后执行以下操作步骤。 1.下载 curl -O https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh2.安装 sh Miniconda3-latest-Linux-x86_64.…...

解密京东面试:如何应对Redis缓存穿透?

亲爱的小伙伴们&#xff0c;大家好&#xff01;欢迎来到小米的微信公众号&#xff0c;今天我们要探讨一个在面试中可能会遇到的热门话题——Redis缓存穿透以及如何解决它。这个话题对于那些渴望进入技术领域的小伙伴们来说&#xff0c;可是必备的哦&#xff01; 认识Redis缓存…...

#力扣:1. 两数之和@FDDLC

1. 两数之和 - 力扣&#xff08;LeetCode&#xff09; 一、Java import java.util.HashMap;class Solution {public int[] twoSum(int[] nums, int target) { //返回数组HashMap<Integer, Integer> map new HashMap<>(); //键&#xff1a;元素值&#xff1b;值&…...

【小沐学Python】各种Web服务器汇总(Python、Node.js、PHP、httpd、Nginx)

文章目录 1、Web服务器2、Python2.1 简介2.2 安装2.3 使用2.3.1 http.server&#xff08;命令&#xff09;2.3.2 socketserver2.3.3 flask2.3.4 fastapi 3、NodeJS3.1 简介3.2 安装3.3 使用3.3.1 http-server&#xff08;命令&#xff09;3.3.2 http3.3.3 express 4、PHP4.1 简…...

【AI视野·今日Robot 机器人论文速览 第四十六期】Tue, 3 Oct 2023

AI视野今日CS.Robotics 机器人学论文速览 Tue, 3 Oct 2023 Totally 76 papers &#x1f449;上期速览✈更多精彩请移步主页 Interesting: &#x1f4da;Aerial Interaction with Tactile, 无人机与触觉的结合&#xff0c;实现空中交互与相互作用。(from CMU) website&#…...

macOS三种软件安装目录以及环境变量优先级

一、系统自带应用 这些软件&#xff08;以git为例&#xff09;位于根目录下的/usr/bin/xxx&#xff0c;又因为系统级环境变量文件/etc/paths已指定了命令查找位置&#xff1a; /usr/local/bin /System/Cryptexes/App/usr/bin /usr/bin /bin /usr/sbin /sbin所以这些自带应用可…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...