现代C++ 实现单例模式

传统写法有什么问题

如果你了解过单例模式,双重检查锁定模式(Double-Checked Locking Pattern,后文简称DCLP)的写法你一定不会陌生,甚至你或许认为它是最正确的代码。

class Singleton {
public://获取单例Singleton* GetInstance() {//双重检查if (p_instance==nullptr) {_mutex.lock();if (p_instance == nullptr)p_instance = new Singleton();_mutex.unlock();}return p_instance;}
private://私有构造函数Singleton() = default;
public:~Singleton() = default;
private://删除构造方法Singleton& operator=(const Singleton& obj) = delete;Singleton(const Singleton& obj) = delete;Singleton& operator=(const Singleton&& obj) = delete;Singleton(const Singleton&& obj) = delete;
private:static Singleton* p_instance;mutex _mutex;
};
Singleton* Singleton::p_instance = nullptr;//类外初始化

这几乎是最常见的单例模式,不过上面的代码却有大问题

问题就出现在这一句:

				p_instance = new Singleton();

该问题在Effective C++的作者Scott Meyers关于单例的论文中被深刻讨论,如果你之前对单例模式缺乏了解,这篇论文你一定需要阅读:

C++ and the Perils of Double-Checked Lockingicon-default.png?t=N7T8https://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

其中提到,要执行该表达式 p_instance = new Singleton(); 需要做三件事:

  1. 开辟出用于存放单例的内存
  2. 在开辟的内存上构造Singleton对象
  3. 使p_instance指针指向开辟的内存

你期望,按照上述步骤去实例化单例,可是编译器没有任何约束去按你期望的顺序执行!

在特殊情况下,编译器有时会调换步骤二和步骤三,就像如下代码:

	Singleton* GetInstance() {//双重检查if (p_instance==nullptr) {_mutex.lock();if (p_instance == nullptr) {p_instance = static_cast<Singleton*>	 //步骤三(operator new(sizeof(Singleton)));//步骤一new(p_instance) Singleton;			        //步骤二}_mutex.unlock();}return p_instance;}

此时执行顺序变为步骤一,步骤三,步骤二

如果你还没有理解,设想有如下场景:

  • 线程A 进入GetInstance,执行完判断,上锁,执行步骤三和步骤一。此时p_instance已经是一个非空的指针,但Singleton单例还未在p_instance所指向的空间上初始化。
  • 线程B 进入GetInstance,判断p_instance为非空,随后把p_instance返回。调用处对p_instance解引用,直接G!访问未初始化的内存。

DCLP只会在步骤一,步骤二在步骤三之前执行才能奏效,但他们的执行顺序确是未定义行为!编译器在这一点上不受任何约束,完全可能导致意想不到的问题。

所以用DCLP实现的单例模式,单例对象的初始化顺序不确定。这种情况可能导致在一个线程中访问尚未初始化的单例对象,从而引发错误。 并不是线程安全,因为包含未定义行为。

现代C++实现单例模式

为了规避这个问题,可利用C++11引入 魔法静态变量 特性,主要描述了具有静态存储期或线程存储期的块作用域变量的初始化规则。

§6.7 [stmt.dcl] p4原文: If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

即,如果在变量(具有静态存储期或线程存储期的块作用域的)初始化过程中同时并发地再次进入声明,那么并发执行必须等待初始化过程完成。

§6.7 [stmt.dcl] p4http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

利用这项标准,你就可以很爽的写出比DCLP更好的代码!

代码样例:

class Singleton {
public://现代写法Singleton& GetInstance() {// Let's create a magic staticstatic Singleton instance;//C++11return instance;//return refence}
private://私有构造函数Singleton() = default;
public:~Singleton() = default;
private://删除构造方法Singleton& operator=(const Singleton& obj) = delete;Singleton(const Singleton& obj) = delete;Singleton& operator=(const Singleton&& obj) = delete;Singleton(const Singleton&& obj) = delete;
};

现代写法使用使用局部静态变量来实现单例模式。具体而言,文章提出的Meyers' Singleton就是基于这一思想的解决方案。

如果本文帮助到你,希望能点赞收藏,欢迎留言讨论。

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

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

相关文章

解决:Component name “index“ should always be multi-word

原因 要求组件名称以驼峰格式命名&#xff0c;自定义组件名称应该由多单纯组成&#xff0c;防止和html标签冲突&#xff0c;所以index.vue 会报错 解决 1、按照规则驼峰格式&#xff0c;如&#xff1a;appIndex.vue 2、若有.eslintrc.js文件&#xff0c;并在规则中(rules)关…

数据挖掘-08-基于Python实现时间序列分析建模(ARIMA 模型)(包括数据和代码)

文章目录 0. 数据代码下载1. 背景描述2. 预测目的3. 数据总览4. 数据预处理4.1数据描述性统计与清洗a. 导入程序库b. 读取数据c. 查看统计信息和空值d. 查看是否有重复数据以及清理重复数据e. 空值清理f. 针对清洗后的数据进行统计分析 5. 探索性数据分析5.1 数据分析 6. 构建 …

猫粮哪个牌子质量好性价比高?盘点十款主食冻干猫粮品牌排行榜!

在过去的100多年里&#xff0c;猫咪主食市场一直被膨化猫粮主导。然而&#xff0c;随着猫咪频频出现猝死、失明、发育不良以及营养不良等问题&#xff0c;猫主人们开始质疑膨化粮是否最适合猫咪。于是&#xff0c;从上世纪90年代开始&#xff0c;出现了生骨肉喂养。生骨肉确实是…

网络安全——基于Snort的入侵检测实验

一、实验目的要求&#xff1a; 二、实验设备与环境&#xff1a; 三、实验原理&#xff1a; 四、实验步骤&#xff1a; 五、实验现象、结果记录及整理&#xff1a; 六、分析讨论与思考题解答&#xff1a; 七、实验截图&#xff1a; 一、实验目的要求&#xff1a; 1、掌握…

一、win10+yolov8+anaconda环境部署

1、安装anaconda &#xff08;1&#xff09;打开aonconda下载地址&#xff1a;https://www.anaconda.com/download&#xff0c;点击download下载。 2、下载完成后&#xff0c;双击打开&#xff0c;点击Next&#xff0c;I Agree&#xff0c;选择just me&#xff1b; 3、勾选…

Spring上下文之注解模块ConfigurationMethod

博主介绍:✌全网粉丝5W+,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验✌ 博主作品:《Java项目案例》主要基于SpringBoot+MyBatis/MyBatis-plus+…

飞天使-docker知识点8-docker的资源限制

文章目录 容器资源限制示例 容器资源限制 Docker提供了多种资源限制的方式&#xff0c;可以根据应用程序的需求和系统资源的可用性进行选择。以下是一些常见的Docker资源限制及其使用情况&#xff1a;CPU限制&#xff1a;通过设置CPU的配额&#xff08;quota&#xff09;和周期…

网络(九)CanSM及达芬奇配置

【小猫爪】AUTOSAR学习笔记05-Communication Stack之CanSM模块-CSDN博客 上链接讲的非常好。 CanSM提供的函数。 C CanSM使用的函数&#xff1a;

spring mail 邮件发送demo

首先配置号邮件服务器&#xff08;我用的是126邮箱&#xff09;&#xff0c;获取密钥&#xff1a; 进入下图的【邮箱中心】->点击右侧的齿轮按钮 接着&#xff0c;开启 下图位置 的 POP3/SMTP&#xff08;我当时点击开启时&#xff0c;需要手机扫码认证下&#xff09; 最后…

putIfAbsent、computeIfAbsent、computeIfPresent

putIfAbsent 判断是否存在&#xff0c;不存在则设置 hashmap.putIfAbsent(K key, V value) 例子如下&#xff1a; public static void main(String[] args) {//hashmap.putIfAbsent(K key, V value)HashMap hashMap Maps.newHashMap();hashMap.put("aa","on…

ConnectionError怎么解决

文章目录 解决思路lz的具体解决过程 解决思路 这个错误表明在尝试加载评价指标时&#xff0c;代码试图从 huggingface 下载文件&#xff0c;但由于代理错误而无法连接。 为了解决这个问题&#xff0c;你可以尝试以下几个步骤&#xff1a; 1.设置代理&#xff1a; 如果你在使…

Zabbix监控系统部署与管理

zabbix介绍 zabbix是⼀个基于 Web 界面的提供分布式系统监视以及网络监视功能的企业级的免费开源解决⽅案。zabbix能监视各种⽹络参数&#xff0c;保证服务器系统的安全运营&#xff1b;并提供灵活的通知机制以让系统管理员快速定位/解决存在的各种问题。 zabbix构成 zabbix由…

Windows进程机制

进程 进程要做任何事情&#xff0c;必须让一个线程在它的上下文运行。该线程负责执行进程地址空间包含的代码。每个进程至少要有一个线程来执行进程地址空间包含的代码。当系统创建一个进程的时候&#xff0c;会自动为进程创建第一个线程&#xff0c;这称为主线程&#xff08;…

命令调用先构建hashTable

GPT 代码改 #include <stdio.h> #include <stdlib.h> #include <string.h>#define TABLE_SIZE 256struct Node {char *key;void *value;struct Node *next; };struct HashTable {struct Node *table[TABLE_SIZE]; };void initHashTable(struct HashTable *ha…

GPT-AI导航

1. https://ai-bot.cn/ https://ai-bot.cn/

工业固体废物智能化综合管控平台

工业固体废物智能化综合管控平台&#xff0c;涵盖产废企业、运输企业、固废处置企 业等不同群体应用&#xff0c;根据不同群体设计不同的业务应用子系统功能&#xff0c;以及各个不 同群体的环保物联网平台子系统功能模块&#xff0c;同时具有移动端的应用APP。 建立产废企业端…

C++ stringOJ练习题

目录 把字符串转换成整数 反转字符串 字符串中的第一个唯一字符 字符串最后一个单词的长度 找出字符串中第一个只出现一次的字符 字符串相加 字符串最后一个单词长度 字符串相乘 反转字符串3 反转字符串2 验证回文串 把字符串转换成整数 通过遍历字符串并逐位转换…

phpy 连接 PHP与Python生态 跨界合作 PHPY搭建 已解决

目录 需求介绍 安装 windows版本 文件地址 运行效果 需求介绍 在日常功能开发中&#xff0c;难免会使用python的计算库&#xff0c;同时自己要是一名PHP开发工程师。就在最近有相应的需求&#xff0c;索性使用phpy来进行功能开发 安装 windows版本 phpy 是识沃团队最新推出…

千梦网创:Too Young,to simple

大多数人啊&#xff0c;还是too young&#xff0c;包括我。 网上的评论对我而言并不影响我通过提供价值服务经营生活&#xff0c;但是有时候对于我的思考还是有一些帮助的。 我把很多可以争与不可争的事件看做是一种现象&#xff0c;这种现象往往可以给予我新的能量。 当学员…