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

c++设计模式之单例设计模式

💂 个人主页:[pp不会算法v](https://blog.csdn.net/weixin_73548574?spm=1011.2415.3001.5343)
🤟 版权: 本文由【pp不会算法^v^】原创、在CSDN首发、需要转载请联系博主
💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦

c++设计模式系列文章

什么是单例设计模式


顾名思义,所谓单例设计模式,就是在这个应用程序中某个类只有一个实例,程序中的所有模块共用这个模块

举几个经常用到单例设计模式的应用场景:
1、网络编程的时候我们将socket,各种网络操作...封装成一个类,那么这个类一般只有一个实例
2、数据库封装类,这个一般也是只有一个实例
3、我上次使用javafx开发一个IM项目的时候对于好友列表这个gui控件(自定义的)也用到了单例设计模式,以为整个应用程序就这一个好友列表
..................剩下的大家可以根据自己的开发经验分享在评论区


什么是单例设计模式中的饿汉式和懒汉式


<font color="red">饿汉式:</font>程序一开始就创建这个实例

<font color="red">懒汉式:</font>第一次调用getInstance()的时候才创建这个实例

其实这个很好记:饿汉他很饥饿,可定很急着去吃东西,所以才急着我还没有去主动获取,他就在程序开始的时候创建实例了;懒汉很懒,所以她一点都不急,不主动,那么只有我们主动找他要的时候太才会创建实例

 c++中单例设计模式的实现

内部类析构


因为这个类只有一个实例那么我们就要防止在外部去创建这个类的实例(对象)就要私有化这个类的构造函数
又因为要保证这个实例不会被外部显示析构,我们还要将析构函数私有化


Single.h:
class Single{
public:
static Single*getSingleInstance()
{
if(m_instance==NULL)
m_instance=new Single();
return m_instance;
}
private:
Single(){}
Single(const Single&);
Single& operator=(const Single&);
~Single(){}
static void deleteSingleInstance()
{
if(m_instance!=NULL)
{
Single*instance=m_instance;
m_instance=NULL;
delete instance;
}
}
class Deletor{
public:
Deletor()
{
//这里你可以创建这个类的实例,也可以不创建,创建那么就是饿汉式,不创建就是懒汉式
}
~Deletor()
{
deleteSingleInstance();
}
}
static Single*m_instance;
static Deletor deletor;
}
Single.cpp:
Single* Single::m_instance=NULL;
Single::Deletor Single:: deletor;


 

这里的Deletor的静态对象deletor就解决了内存泄漏的问题,这个内部类对象外部也访问不到,而它又是一个静态的变量,我们知道c++应用程序在 结束之前会逆序析构静态变量,而这个类的析构是public那么就能自动析构,析构的时候就释放了单例的堆内存,然后你将这个类的一些操作封装在public下,那么这个单例就已经不需要你管了,你要用的时候获取它的指针就行了,

 智能指针析构


上面这种使用内部类的方式看着有一丢丢麻烦,而且不是很优雅
c++给我们提供了一个很好用的工具:智能指针

智能指针简介
更加具体的介绍请自行去查阅资料,这里面大有文章


C++ 智能指针是一种用于管理动态分配的对象内存的工具,可以帮助避免内存泄漏和悬空指针等问题。C++11 引入了两种主要类型的智能指针:std::shared_ptr 和 std::unique_ptr。

std::shared_ptr:代表一个共享所有权的智能指针。多个 shared_ptr 对象可以同时拥有一个对象,并在不再需要时自动释放对象内存。它使用引用计数的方式来追踪有多少个 shared_ptr 共享同一对象的所有权。当引用计数减为零时,对象内存将被释放。
#include <memory>
std::shared_ptr<Type> sharedPtr = std::make_shared<Type>(args);

std::unique_ptr:代表一个独占所有权的智能指针。只能有一个 unique_ptr 指向一个对象,不能进行拷贝构造或赋值操作。当 unique_ptr 被销毁或重置时,它所拥有的对象内存会被释放。
#include <memory>
std::unique_ptr<Type> uniquePtr = std::make_unique<Type>(args);


通过使用智能指针,可以避免显式地调用 delete 或 delete[] 来释放动态分配的对象内存,从而减少内存泄漏的风险。可以使用智能指针提供的成员函数和操作符来访问和操作所拥有的对象。另外,还可以自定义删除器函数来指定动态释放对象内存的方式。

除了 shared_ptr 和 unique_ptr,C++ 还提供了 std::weak_ptr、std::auto_ptr(C++11 之前的版本)等其他类型的智能指针,每种都有不同的所有权和语义特点,适用于不同的场景。选择合适的智能指针类型可以提高代码的安全性和可维护性。


那么我们就用智能指针来修改上面使用内部类的方式实现的单例设计模式
 

Single.h:
#include <memory>//使用智能指针要包含的头文件
class Single{
public:
static Single*getSingleInstance()
{if (m_instance == NULL)m_instance = std::make_unique<Single>();return m_instance.get();
}
private:
Single(){}
Single(const Single&);
Single& operator=(const Single&);
~Single(){}
static std::unique_ptr<Single> m_instance;
}
Single.cpp:
std::unique_ptr<Single> Single::m_instance=NULL;


单例模式的线程安全问题


首先我们要知道单例模式什么时候会出现线程安全问题,其实就只有一个时刻,就是创建实例的时候,创建之后就不存在线程安全的问题

首先最简单的避免这个问题的方法就是饿汉式,在只有主线程的时候就创建了实例,也就不存在线程安全问题了

但是怎么解决懒汉式的线程安全问题呢?

当实例未创建的时候多个线程同时去请求这个实例,那么都通过了=NULL的判断,那么就会重复创建实例,为了解决这个问题我罗列处一下几种处理方法

1、一次判断+互斥锁

Single*getInstance()
{
//加锁
mutex.lock();
if(m_instance==NULL)
m_instance=new Single();
//解锁
mutex.unlock();
return m_instance;
}

2、双重检测锁模式(DCL:Double-Checked Locking Pattern

Single* getInstance()
{
if(m_instance==NULL){mutex.lock();if(m_instance==NULL)m_instance=new Single();mutex.unlock();}
return m_instance;
}

为什么要检测两次呢,相较于第一种方法这不是多次一举吗,开始我看到也是这么认为的,但是后来一想,线程安全只会在创建实例的时候出现第一种方法就算创建之后多线程同时获取实例的时候也会产生竞争产生等待那么是不是除了创建实例的时候的等待是有意义的后面的等待都是多余的了纯纯是消耗性能了,再看这个DCL机制,当我们创建完成之后还会等待吗,根本不会因为第一次检测就通过了,直接返回实例的指针了,不会进入锁的区域,但是没创建的时候就会竞争等待,这样就解决了第一种方式的弊端

3、原子操作

思考一下第二种方式还有没有潜在的问题存在呢?

有的,c++中new一个对象的全过程:

在 C++ 中,new 一个对象的过程确实可以分为三个阶段:分配内存空间、初始化成员变量和调用构造方法。

  1. 分配内存空间:在使用 new 运算符时,会首先从堆(Heap)中分配一块合适大小的内存空间用于存储对象的数据。

  2. 初始化成员变量:在分配内存空间后,编译器会根据对象的定义,对对象的成员变量进行初始化。这包括调用各个成员变量的默认构造函数或者使用初始化列表进行初始化。

  3. 调用构造方法:在成员变量初始化完成之后,会调用对象的构造方法,对对象进行进一步的初始化操作。构造方法是类的特殊成员函数,用于完成对象的初始化工作。

在上述过程中,当分配内存空间完成后,对象的指针就不再为空了。具体来说,当成功分配内存空间并返回指针时,该指针指向的内存地址即为对象的有效地址,可以开始对对象进行访问和操作。

那么问题就来了,可能一个线程拿到了锁开始new出实例但是还只完成了第一步分配内存空间,但是此时m_instance就已经不为NULL了,那么别的线程如果此时获取实例那么第一次检测就通过了那么得到的实例就是没有完成初始化和构造的实例这就会造成严重的错误甚至导致程序崩溃

所以我们要保证要么就彻底创建实例要么就不创建,这就涉及到原子操作了                                                               v         

atomic<Single*> Single::m_instance;//记得在源文件初始化这里我就不写了getInstance() {if (m_instance == NULL) { mutex.lock();if (pInstance == nullptr) { pInstance = new Widget(); }mutex.unlock();} return m_instance;
}

但是上面再判断的时候使用原子类型也会进行原子操作但是这是没必要的我们只需要在创建实例的时候进行原子操作就行了所以可以优化一下:


Single* getInstance() {Single* p = m_instance;if (p == nullptr) { mutex.lock();if ((p = pInstance) == NULL) { m_instance= p = new Single(); }mutex.unlock();} return p;
}

参考了很多别人的文章,如果有不对的还请指正,如果后面有更好的方法再补充吧....

相关文章:

c++设计模式之单例设计模式

&#x1f482; 个人主页:[pp不会算法v](https://blog.csdn.net/weixin_73548574?spm1011.2415.3001.5343) &#x1f91f; 版权: 本文由【pp不会算法^v^】原创、在CSDN首发、需要转载请联系博主 &#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦…...

App自动化测试环境搭建

目录 1、java jdk安装 2、node.js安装 3、安装模拟器安装 4、Android SDK 安装 5、Appium-Server安装 6、appium客户端安装  7、Appium-Python-Client安装 只做记录和注意点&#xff0c;详细内容不做解释 环境&#xff1a;winappium夜神模拟器python 需要用到的工具&a…...

win10搭建gtest测试环境+vs2019

首先是下载gtest&#xff0c;这个我已经放在了博客上方资源绑定处&#xff0c;这个适用于win10vs版本&#xff0c;关于liunx版本的不能用这个。 或者百度网盘链接&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/15m62KAJ29vNe1mrmAcmehA 提取码&#xff1a;vfxz 下…...

【代码随想录】算法训练营 第二天 第一章 数组 Part 2

977. 有序数组的平方 题目 暴力解法 思路 原地更新所有数组元素为其平方数后&#xff0c;再使用sort函数排序&#xff0c;对vector使用sort函数时&#xff0c;两个参数分别是vector的起始元素和终止元素。 代码 class Solution { public:vector<int> sortedSquares(…...

在深度学习中,累计不同批次的损失估计总体损失

在深度学习中&#xff0c;累计不同批次的损失估计总体损失 在深度学习训练模型的过程中&#xff0c;通常会通过计算不同批次间的损失和&#xff0c;当作模型在这个训练集上的总体损失&#xff0c;这种做法是否具有可行性呢&#xff1f; 什么是总体损失? 总体损失是计算模型在…...

论文导读|八月下旬Operations Research文章精选:定价问题专题

编者按&#xff1a; ​ ​在“ Operations Research论文精选”中&#xff0c;我们有主题、有针对性地选择了Operations Research中一些有趣的文章&#xff0c;不仅对文章的内容进行了概括与点评&#xff0c;而且也对文章的结构进行了梳理&#xff0c;旨在激发广大读者的阅读兴…...

(三)Apache log4net™ 手册 -演示

0、引言 在开始本文之前&#xff0c;推荐您首先阅读 Apache log4net™ 手册中有关 介绍 与 配置 的相关内容。本文将通过实践分别为您演示如何使用 Visual Studio 2022 在 .NET Framework 项目和 .NET 项目下配置并使用 Log4Net。 1、为 .NET Framework 项目配置 Log4Net 1.1…...

VScode远程root权限调试

尝试诸多办法无法解决的情况下&#xff0c;允许远程登陆用户直接以root身份登录 编辑sshd_config文件 sudo vim /etc/ssh/sshd_config 激活配置 注释掉PermitRootLogin without-password&#xff0c;即#PermitRootLogin without-password 增加一行&#xff1a;PermitRootLo…...

【ARM CoreLink 系列 7 -- TZC-400控制器简介】

文章目录 背景介绍1.1 TZC-400 简介1.2 TZC-400 使用示例1.3 TZC-400 interfaces1.3.1 FPID1.3.2 NSAID Regionregion 检查规则 1.4 Features1.5 Register summary1.6 TZC-400和TZPC和TZASC区别 背景介绍 为了确保内存能够正确识别总线的信号控制位&#xff0c;新增一个TrustZ…...

【C++】-c++11的知识点(中)--lambda表达式,可变模板参数以及包装类(bind绑定)

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …...

浅析倾斜摄影三维模型(3D)几何坐标精度偏差的几个因素

浅析倾斜摄影三维模型&#xff08;3D&#xff09;几何坐标精度偏差的几个因素 倾斜摄影是一种通过倾斜角度较大的相机拍摄建筑物、地形等场景&#xff0c;从而生成高精度的三维模型的技术。然而&#xff0c;在进行倾斜摄影操作时&#xff0c;由于多种因素的影响&#xff0c;导致…...

【广州华锐互动】智轨列车AR互动教学系统

智轨列车&#xff0c;也被称为路面电车或拖电车&#xff0c;是一种公共交通工具&#xff0c;它在城市的街头巷尾提供了一种有效、环保的出行方式。智轨列车的概念已经存在了很长时间&#xff0c;但是随着科技的发展&#xff0c;我们现在可以更好地理解和欣赏它。通过使用增强现…...

驾驶数字未来:汽车业界数字孪生技术的崭新前景

随着数字化时代的到来&#xff0c;汽车行业正经历着前所未有的变革。数字孪生技术&#xff0c;作为一种前沿的数字化工具&#xff0c;正在为汽车行业带来革命性的影响&#xff0c;不仅改变着汽车制造和维护的方式&#xff0c;也为消费者带来了前所未有的体验。让我们一起探讨&a…...

JVM 性能调优参数

JVM分为堆内存和非堆内存 堆的内存分配用-Xms和-Xmx -Xms分配堆最小内存&#xff0c;默认为物理内存的1/64&#xff1b; -Xmx分配最大内存&#xff0c;默认为物理内存的1/4。 非堆内存分配用-XX:PermSize和-XX:MaxPermSize -XX:PermSize分配非堆最小内存&#xff0c;默认为物理…...

11在SpringMVC中响应到浏览器的数据格式,@ResponseBody注解和@RestController复合注解的功能详解

响应数据/转发或重定向页面 参考文章数据交换的常见格式,如JSON格式和XML格式 服务器将接收到请求处理完以后需要将处理结果告知给浏览器即响应,通常有响应要转发/重定向到的页面和响应数据(文本数据/json数据)两种方式 如果控制器方法返回值类型为void并且没有通过response…...

go 流程控制之switch 语句介绍

go 流程控制之switch 语句介绍 文章目录 go 流程控制之switch 语句介绍一、switch语句介绍1.1 认识 switch 语句1.2 基本语法 二、Go语言switch语句中case表达式求值顺序2.1 switch语句中case表达式求值次序介绍2.2 switch语句中case表达式的求值次序特点 三、switch 语句的灵活…...

sql 时间有偏差的解决方法

测试功能的时候发现记录的创建时间不对&#xff0c;死活对不上&#xff0c;下意识的以为是服务器时间有偏差&#xff0c;后来排查发现存入表中的时间是正常的&#xff0c;但是查询展示出来的时间是不对的&#xff0c;就去排查可能是查询sql格式化时间有问题&#xff0c;果不其然…...

Apache Lucene 7.0 - 索引文件格式

Apache Lucene 7.0 - 索引文件格式 文章目录 Apache Lucene 7.0 - 索引文件格式介绍定义反向索引字段类型段文档数量索引结构概述文件命名文件扩展名摘要锁文件 原文地址 介绍 这个文档定义了在这个版本的Lucene中使用的索引文件格式。如果您使用的是不同版本的Lucene&#xf…...

GEE:使用中文做变量和函数名写GEE代码

作者&#xff1a;CSDN _养乐多_ 啊&#xff1f;最近在编写GEE代码的时候&#xff0c;无意中发现 JavaScript 已经能够支持中文字符作为变量名和函数名&#xff0c;这个发现让我感到非常兴奋。这意味着以后在编程过程中&#xff0c;我可以更自由地融入中文元素&#xff0c;不再…...

针对量化交易SDK的XTP的初步摸索

这东西只要是调用API实现自动交易股票的&#xff0c;就不可能免费的接口。 并且用这些接口实现自动交易还得 归证券公司监管。比如 xtp出自 中泰证券&#xff0c;那么如果用xtp实现自动交易股票的软件&#xff0c;具体操作实盘的时候 不能跑再自己的电脑上&#xff0c;必须跑在…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...