当前位置: 首页 > 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所以这些自带应用可…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

接口自动化测试:HttpRunner基础

相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具&#xff0c;支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议&#xff0c;涵盖接口测试、性能测试、数字体验监测等测试类型…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...