[C/C++] -- 单例模式

1.简介

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

引入单例模式目的:

  • 全局访问点: 单例模式提供了一个全局访问点,使得任何地方都可以方便地访问该实例,而不需要通过传递对象的方式。

  • 节省资源: 在某些情况下,创建类的实例可能会消耗较多的资源,例如数据库连接、线程池等。通过使用单例模式,可以避免频繁地创建和销毁对象,从而节省资源和提高性能。

  • 确保唯一性: 单例模式确保类的实例只有一个,避免了因多次创建实例而导致的数据不一致或状态不同步的问题。

  • 控制实例化过程: 单例模式将类的实例化过程集中在一个地方,使得可以更加灵活地控制实例化的时机和方式,例如延迟实例化、懒加载等。

  • 提供全局服务: 单例模式常用于提供全局服务或管理全局状态,例如日志记录器、配置管理器、线程池等。

线程安全

在多线程环境中,确保单例模式的线程安全性至关重要。如果不考虑线程安全性,在多个线程同时调用 getInstance() 方法时,可能会导致多个实例被创建,违反了单例模式的原则。

在C++中,可以通过以下几种方式来确保单例模式的线程安全性:

  • 使用静态初始化: 在C++11及以后的标准中,静态局部变量的初始化是线程安全的。因此,可以将单例实例声明为一个静态局部变量,并在 getInstance() 方法中返回该变量。这样做可以保证在首次调用 getInstance() 方法时,单例实例会被线程安全地初始化。

  • 加锁: 可以使用互斥量(mutex)来实现加锁机制,确保在同一时间只有一个线程可以执行关键代码段。在 getInstance() 方法中,使用互斥量对实例化过程进行加锁,从而确保在多线程环境中只有一个实例被创建。

  • 双重检查锁定(Double-Checked Locking): 双重检查锁定是一种常见的在多线程环境下实现延迟初始化的方法。在 getInstance() 方法中,首先检查实例是否已经被创建,如果尚未创建,则使用互斥量进行加锁,并再次检查实例是否已经被创建。这种方式可以减少在每次调用 getInstance() 方法时都进行加锁的开销。

  • 使用原子操作: C++11引入了一系列的原子操作,如 std::atomic 类和相关的原子操作函数,可以在不需要显式加锁的情况下实现线程安全的操作。可以将单例实例声明为原子类型,从而确保对实例的访问是线程安全的。

2.单例模式实现

单例模式常见以下两类

饿汉式单例模式:还没有获取实例对象,实例对象已经产生

懒汉式单例模式:唯一的实例对象,直到第一次获取它的时候才产生

饿汉式单例模式

在类加载的时候就创建实例,保证了线程安全,但可能会造成资源浪费,因为实例在程序运行期间始终存在,即使没有被使用。

class Singleton
{
public:static Singleton *getInstance() //3.获取类的唯一实例对象的接口方法{return &instance;}
private:static Singleton instance;      //2.定义一个唯一的类的实例对象Singleton()                     //1.构造函数私有化{}//确保单例模式只有一个实例存在Singleton(const Singleton &) = delete;//禁用拷贝构造函数Singleton &operator=(const Singleton &) = delete;//禁用拷贝赋值运算符
};
Singleton Singleton::instance;      //静态成员变量在类外初始化int main()
{Singleton *p1 = Singleton::getInstance();Singleton *p2 = Singleton::getInstance();Singleton *p3 = Singleton::getInstance();//Singleton t = *p1;无法访问,因为拷贝构造已被禁用return 0;
}

懒汉式单例模式

在第一次使用时才进行实例化,避免了资源浪费,但需要考虑线程安全性。常见的线程安全的懒汉式实现方式包括使用加锁的方式或双重检查锁定。

//懒汉式单例:唯一的实例对象,直到第一次获取它的时候才产生
std::mutex mtx;
//懒汉式单例模式-》是不是线程安全的呢?-》线程安全的懒汉式单例模式
class Singleton
{
public://是不是可重入函数?    锁+双重判断static Singleton *getInstance() //3.获取类的唯一实例对象的接口方法{//lock_guard<std::mutex> guard(mtx);//锁的粒度太大了if(instance==nullptr){lock_guard<std::mutex> guard(mtx);if(instance ==nullptr){/*开辟内存构造对象给instance赋值*/instance = new Singleton();}}return instance;}
private://volatile多线程环境获取到的都是内存的值,变量值可以及时更新static Singleton *volatile instance;      //2.定义一个唯一的类的实例对象Singleton()                     //1.构造函数私有化{}//确保单例模式只有一个实例存在Singleton(const Singleton &) = delete;//禁用拷贝构造函数Singleton &operator=(const Singleton &) = delete;//禁用拷贝赋值运算符
};
Singleton*volatile Singleton::instance=nullptr;      //静态成员变量在类外初始化int main()
{Singleton *p1 = Singleton::getInstance();//第一次进来才new对象Singleton *p2 = Singleton::getInstance();Singleton *p3 = Singleton::getInstance();return 0;
}

不使用互斥锁的线程安全的单例模式

C++11及之后的标准中,静态局部变量的初始化会在多线程环境下进行线程安全的操作,避免了竞态条件。因此,尽管没有显式地使用互斥锁来保护临界区,但由于静态局部变量的初始化机制,仍然确保了在多线程环境下只有一个实例被创建。

class Singleton
{static Singleton *getInstance() //3.获取类的唯一实例对象的接口方法{//g++ -o run 单例模式.cpp -g gdb run//函数静态局部变量初始化,在汇编指令上已经自动添加线程互斥指令了static Singleton instance;//静态成员变量在数据段上,只有第一次调用时才初始化,对象初始化调用构造函数return &instance;}
private:Singleton()                     //1.构造函数私有化{//很多初始化代码}//确保单例模式只有一个实例存在Singleton(const Singleton &) = delete;//禁用拷贝构造函数Singleton &operator=(const Singleton &) = delete;//禁用拷贝赋值运算符
};int main()
{Singleton *p1 = Singleton::getInstance();//第一次进来才new对象Singleton *p2 = Singleton::getInstance();Singleton *p3 = Singleton::getInstance();return 0;
}

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

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

相关文章

掌握JavaScript ES2023新特性

截至2023年底&#xff0c;ECMAScript 2023&#xff08;也称为ES2023&#xff09;的一些新特性已经被确定并纳入了JavaScript的最新标准。这些新特性继续增强了JavaScript的功能性和表达力。下面是一些值得关注的ES2023新特性&#xff1a; ### 1. Record 和 Tuple Record和Tup…

信息泄露中的目录遍历,phpinfo,备份文件下载

一、目录遍历漏洞 1.什么是目录遍历漏洞 指的是在没有授权的情况下读取文件&#xff0c;某些情况下还可对服务器里的文件任意写入 2.目录遍历漏洞成因 网站配置存在缺陷&#xff0c;对输入目录缺少验证&#xff0c;没过滤../之类的目录跳转符&#xff0c;可通过提交目录跳转…

cesium模型路径漫游实现

开始调用模拟飞行&#xff1a;&#xff08;viewer是初始化地图容器对象&#xff0c;具体可参考文章&#xff1a;Cesium初始化地图对象容器配置项汇总_cesium 初始化容器大小-CSDN博客&#xff09; import flyModelRoam from "../utils/GISRoam";// 开始模拟飞行let f…

超详细——集成学习——Adaboost——笔记

资料参考 1.【集成学习】boosting与bagging_哔哩哔哩_bilibili 集成学习——boosting与bagging 强学习器&#xff1a;效果好&#xff0c;模型复杂 弱学习器&#xff1a;效果不是很好&#xff0c;模型简单 优点 集成学习通过将多个学习器进行结合&#xff0c;常可获得比单一…

代码整洁之道第3章-函数

五一假期结束了, 今天继续读一下第三章:函数的相关内容, 其实函数的相关内容设计到的东西很多, 想把一个函数写好也是很难的; 还是按照之前的样子, 先总结一下本章内容, 然后聊一下相关的话题 内容总结 函数应该尽量小 ​ 在从业生涯中我见过最长的一个函数是几千行, 那简直就是…

AI绘画ComfyUI工作流安装教程,新手入门安装部署教程

ComfyUI 是专为 Stable Diffusion 打造的图形用户界面&#xff08;GUI&#xff09;&#xff0c;采用了基于节点的操作方式。用户可以通过连接不同的模块&#xff08;即节点&#xff09;来创建复杂的图像生成流程。这些节点涵盖了多样的功能&#xff0c;包括加载检查点模型、输入…

慧天卓特干旱监测系统:2023年云南最强冬春连旱分析

2023年开春以来&#xff0c;由于高温少雨&#xff0c;土壤失墒快&#xff0c;我国西南的云贵川渝等地区出现连续快速干旱&#xff0c;云南地区的干旱尤为严重。 2023年1月下旬至6月18日&#xff0c;云南平均降水量197.7毫米&#xff0c;为1961年以来历史同期最少&#xff0c;气…

生成requirements.txt文件

前言 对于Python项目&#xff0c;生成和使用requirements.txt是十分必要的。通过requirements.txt可以一次性保存和安装项目所需要的所有库。尤其是在复现github上的实验代码时。 方法1 常用的命令 pip freeze > requirements.txt然而这种方法并不好用&#xff0c;有时会…

什么是泛域名证书?与普通SSL证书有什么区别

随着互联网的发展&#xff0c;越来越多的网站开始使用SSL证书来保护用户的隐私和安全。在SSL证书中&#xff0c;泛域名SSL证书和普通域名证书是两种常见的类型。那么&#xff0c;什么是泛域名SSL证书&#xff0c;与普通域名证书有什么区别呢&#xff1f; 首先&#xff0c;我们来…

ChatGPT变懒原因:正在给自己放寒假!已被网友测出

ChatGPT近期偷懒严重&#xff0c;有了一种听起来很离谱的解释&#xff1a; 模仿人类&#xff0c;自己给自己放寒假了&#xff5e; 有测试为证&#xff0c;网友Rob Lynch用GPT-4 turbo API设置了两个系统提示&#xff1a; 一个告诉它现在是5月&#xff0c;另一个告诉它现在是1…

基于改进遗传优化的BP神经网络金融序列预测算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 遗传算法&#xff08;GA&#xff09;原理 4.2 BP神经网络原理 4.3 遗传优化BP神经网络结合应用 4.4 遗传算法简要改进 5.完整程序 1.程序功能描述 基于改进遗传优化的BP神经网络金融…

什么是影响力?HR招聘测评,如何考察候选人的影响力?

什么是影响力&#xff1f; 影响力也即是说服别人同你的观点&#xff0c;或者是潜移默化的改变他人&#xff0c;从而形成自我凝聚力&#xff0c;影响力可以推动某一个事务的进行。影响力尤其在管理型岗位上具有重要作用。 在百科中有如下定义&#xff1a;影响力是用别人乐于接受…

Python类方法探秘:从单例模式到版本控制

引言&#xff1a; 在Python编程中&#xff0c;类方法作为一种特殊的实例方法&#xff0c;以其独特的魅力在众多编程范式中脱颖而出。它们不仅提供了无需实例即可调用的便捷性&#xff0c;还在设计模式、版本控制等方面发挥着重要作用。本文将通过几个生动的示例&#xff0c;带您…

大厂常见算法50题-最小栈

专栏持续更新50道算法题&#xff0c;都是大厂高频算法题&#xff0c;建议关注, 一起巧‘背’算法! 文章目录 题目解法一 遍历找最小值&#xff08;不要求时间复杂度情况下&#xff09;解法二 辅助栈总结 题目 解法一 遍历找最小值&#xff08;不要求时间复杂度情况下&#xff0…

艾体宝方案 | 加密USB金融解决方案

在现代金融行业中&#xff0c;保护敏感数据和合规性已成为至关重要的任务。为了帮助金融公司应对移动性风险和合规挑战&#xff0c;我们提供了一种高效的加密USB解决方案。 一、为什么金融公司需要加密USB解决方案 1、降低移动性风险 金融服务公司正在迅速过渡到一种模式&a…

《深入理解kafka-核心设计与实践原理》

本文是对于《深入理解kafka-核心设计与实践原理》的笔记和提纲整理 主要用于复习和知识点快速复习 第一章&#xff1a;概念 链接&#xff1a;《深入理解kafka-核心设计与实践原理》第一章&#xff1a;概念 第一章&#xff1a;概念 [1.1] 基本概念 [1.1.1] 基本角色与概念[1.1.…

教程分享:如何为跨境电商、外贸、国际展会制作二维码?

不论是做跨境电商、在全球做产品推广&#xff0c;还是国外的餐厅运营、参加国际展会&#xff0c;或者是做创意户外广告、制作个性化的个人名片、有趣的产品包装……只要是在国外使用二维码&#xff0c;你都可以在QR Tiger去制作您需要的二维码&#xff01; 一、认识QR Tiger 二…

SpringBoot+Redission实现排行榜功能

SpringBootRedission实现排行榜功能 demo地址&#xff1a;ranking-demo: 排行榜DEMO (gitee.com) 一、业务需求 实现一个排行榜&#xff0c;要求按照分数和达成这个分数的时间排序&#xff0c;即相同分数下&#xff0c;时间早的在上面 二、Redis中的zSet(有序集合) 1.简介 …

Flutter 中的 @immutable:深入解析与最佳实践

在 Flutter 开发中&#xff0c;immutable 注释扮演着至关重要的角色&#xff0c;用于标记不可变类。不可变类顾名思义&#xff0c;其状态一旦创建便不可更改&#xff0c;这与可变类截然不同。后者允许在创建后对实例进行修改。 immutable 的利好 引入不可变类可以带来诸多优势…

GO日志打印添加goroutineid

今天想给日志添加一个前缀&#xff0c;以区分不同goroutine的日志&#xff0c;方便做并发问题的排查&#xff0c;做日志跟踪。 为了解决goroutineid&#xff0c;网上各出奇招&#xff0c;有的使用runtime包未公开的方法获取&#xff1a; func Goid() int {defer func() {if e…