单例模式-如何保证全局唯一性?
以下是几种实现单例模式并保证全局唯一性的方法:
1. 饿汉式单例模式
class Singleton {
private:// 私有构造函数,防止外部创建对象Singleton() {}// 静态成员变量,存储单例对象static Singleton instance;
public:// 公有静态成员函数,用于获取单例对象static Singleton& getInstance() {return instance;}
};// 静态成员变量的初始化,在程序启动时创建单例对象
Singleton Singleton::instance;
解释:
- 构造函数私有化:将
Singleton类的构造函数声明为private,确保外部无法直接创建该类的对象。 - 静态成员变量:使用
static Singleton instance存储单例对象。 - 静态成员函数:通过
static Singleton& getInstance()提供获取单例对象的接口。 - 全局初始化:在程序启动时,
Singleton::instance就会被创建,因为它是静态成员,且在类外进行了定义和初始化。这保证了在程序运行的任何时刻,getInstance()都能返回同一个对象。
2. 懒汉式单例模式(线程不安全)
class Singleton {
private:// 私有构造函数,防止外部创建对象Singleton() {}// 静态成员变量,存储单例对象static Singleton* instance;
public:// 公有静态成员函数,用于获取单例对象static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton();}return instance;}
};// 静态成员变量的初始化,初始化为 nullptr
Singleton* Singleton::instance = nullptr;
解释:
- 构造函数私有化:与饿汉式相同,将构造函数设为
private。 - 静态指针成员变量:使用
static Singleton* instance存储单例对象的指针,初始化为nullptr。 - 静态成员函数:
getInstance()函数在首次调用时检查instance是否为nullptr,若为nullptr则创建对象,后续调用都将返回同一个对象。 - 线程不安全:这种方式在多线程环境下存在问题,多个线程可能同时检查到
instance为nullptr,并同时创建对象,导致多个实例的出现。
3. 懒汉式单例模式(线程安全,使用互斥锁)
#include <mutex>class Singleton {
private:// 私有构造函数,防止外部创建对象Singleton() {}// 静态成员变量,存储单例对象static Singleton* instance;// 互斥锁,用于线程同步static std::mutex mtx;
public:// 公有静态成员函数,用于获取单例对象static Singleton* getInstance() {std::unique_lock<std::mutex> lock(mtx);if (instance == nullptr) {instance = new Singleton();}return instance;}
};// 静态成员变量的初始化,初始化为 nullptr
Singleton* Singleton::instance = nullptr;
// 静态成员变量的初始化,创建互斥锁
std::mutex Singleton::mtx;
解释:
- 构造函数私有化:确保外部无法直接创建对象。
- 静态指针成员变量:存储单例对象的指针,初始化为
nullptr。 - 互斥锁:使用
std::mutex mtx进行线程同步。 - 静态成员函数:在
getInstance()中,使用std::unique_lock获取互斥锁,确保同一时刻只有一个线程可以创建单例对象。 - 性能问题:这种方式每次调用
getInstance()都需要加锁,即使instance已经创建,会影响性能。
4. 懒汉式单例模式(线程安全,双重检查锁定)
#include <mutex>class Singleton {
private:// 私有构造函数,防止外部创建对象Singleton() {}// 静态成员变量,存储单例对象static Singleton* instance;// 互斥锁,用于线程同步static std::mutex mtx;
public:// 公有静态成员函数,用于获取单例对象static Singleton* getInstance() {if (instance == nullptr) {std::unique_lock<std::mutex> lock(mtx);if (instance == nullptr) {instance = new Singleton();}}return instance;}
};// 静态成员变量的初始化,初始化为 nullptr
Singleton* Singleton::instance = nullptr;
// 静态成员变量的初始化,创建互斥锁
std::mutex Singleton::mtx;
解释:
- 构造函数私有化:防止外部创建对象。
- 静态指针成员变量:存储单例对象指针,初始化为
nullptr。 - 互斥锁:用于线程同步。
- 静态成员函数:使用双重检查锁定,第一次检查
instance是否为nullptr是无锁的,若为nullptr则加锁再次检查并创建对象,避免了每次调用getInstance()都加锁的性能问题。 - 潜在问题:在某些编译器和处理器架构下,由于指令重排,可能导致
instance已经分配内存但未完成构造函数调用,导致其他线程访问到未完全初始化的对象。
5. 懒汉式单例模式(线程安全,C++11 及以上)
#include <memory>
#include <mutex>class Singleton {
private:// 私有构造函数,防止外部创建对象Singleton() {}
public:// 公有静态成员函数,用于获取单例对象static Singleton& getInstance() {static std::once_flag flag;std::call_once(flag, []() {instance.reset(new Singleton());});return *instance.get();}
private:// 静态成员变量,存储单例对象static std::unique_ptr<Singleton> instance;
};// 静态成员变量的初始化,使用智能指针存储单例对象
std::unique_ptr<Singleton> Singleton::instance;
解释:
- 构造函数私有化:防止外部创建对象。
- 静态成员函数:使用
std::once_flag和std::call_once保证单例对象只被创建一次。 - 智能指针:使用
std::unique_ptr<Singleton> instance存储单例对象,避免了内存管理问题。 - C++11 特性:
std::call_once是 C++11 引入的,确保instance只会被调用一次,且是线程安全的,解决了双重检查锁定的指令重排问题。
6、总结
通过以上几种方式,可以实现单例模式并保证全局唯一性。在实际应用中,推荐使用 C++11 及以上的 std::call_once 和 std::unique_ptr 实现,它提供了简洁、安全和高效的单例模式实现方式。
上述几种实现方式都旨在保证单例模式的全局唯一性,但各有优缺点,你可以根据实际需求和开发环境选择合适的实现方式。**代码解释**:
- **饿汉式单例模式**:- 优点:实现简单,在程序启动时就创建单例对象,保证了线程安全和全局唯一性。- 缺点:可能会造成资源浪费,因为单例对象在程序开始时就创建,无论是否使用。- **懒汉式单例模式(线程不安全)**:- 优点:单例对象在首次使用时创建,避免了资源浪费。- 缺点:在多线程环境下无法保证单例对象的唯一性,会出现多个实例。- **懒汉式单例模式(线程安全,使用互斥锁)**:- 优点:使用互斥锁保证了多线程环境下的单例对象唯一性。- 缺点:性能开销大,每次调用 `getInstance()` 都要加锁,即使单例对象已经创建。- **懒汉式单例模式(双重检查锁定)**:- 优点:在多线程环境下相对高效,避免了每次调用都加锁。- 缺点:存在指令重排的潜在风险,可能导致获取到未完全初始化的对象。- **懒汉式单例模式(线程安全,C++11及以上)**:- 优点:结合了 `std::call_once` 和 `std::unique_ptr`,既保证了线程安全,又避免了指令重排问题,是现代 C++ 推荐的实现方式。- 缺点:需要 C++11 及以上标准支持。
相关文章:
单例模式-如何保证全局唯一性?
以下是几种实现单例模式并保证全局唯一性的方法: 1. 饿汉式单例模式 class Singleton { private:// 私有构造函数,防止外部创建对象Singleton() {}// 静态成员变量,存储单例对象static Singleton instance; public:// 公有静态成员函数&…...
设计模式学习笔记——结构型模式
文章目录 适配器模式 Adapter适用场景UML 桥接模式 Bridge适用场景UML 组合模式 Composite装饰模式 Decorator外观模式 Facade享元模式 Flyweight代理模式 Proxy 适配器模式 Adapter 适用场景 希望使用某个类, 但是其接口与其他代码不兼容时, 可以使用…...
WEB攻防-通用漏洞_文件上传_黑白盒审计流程
目录 前置知识点 Finecms-CMS文件上传 编辑 Cuppa-Cms文件上传 Metinfo-CMS 文件上传 前置知识点 思路: 黑盒就是寻找一切存在文件上传的功能应用 1 、个人用户中心是否存在文件上传功能 2 、后台管理系统是否存在文件上传功能 3 、字典目录扫描探针文件上传构…...
RabbitMQ基本介绍及简单上手
(一)什么是MQ MQ(message queue)本质上是队列,满足先入先出,只不过队列中存放的内容是消息而已,那什么是消息呢? 消息可以是字符串,json也可以是一些复杂对象 我们应用场…...
服务器证书不受信任是什么问题?
用户在访问某些网站时,可能会遇到“服务器证书不受信任”的警告。这一问题不仅影响用户的浏览体验,更可能对网站的信誉和安全性产生深远影响。那么服务器证书不受信任是什么问题呢? 服务器证书的基本概念 服务器证书是由证书颁发机构(CA)签…...
spring mvc源码学习笔记之十
前面的文章介绍了用 WebApplicationInitializer 或者 AbstractAnnotationConfigDispatcherServletInitializer 来代替 web.xml 。 我们学 java web 的时候就知道,servlet 容器会自动加载 web.xml。 那么,疑问就来了,WebApplicationInitialize…...
Ubuntu 下载安装 elasticsearch7.17.9
参考 https://blog.csdn.net/qq_26039331/article/details/115024218 https://blog.csdn.net/mengo1234/article/details/104989382 过程 来到 Es 的版本发布列表页面:https://www.elastic.co/downloads/past-releases#elasticsearch 根据自己的系统以及要安装的…...
Qt笔记:网络编程Tcp
一、铺垫 1.以下只是告诉诸位怎样去构建服务器与客户端;客户端这样构建肯定没问题;但是服务端不可能这样写,因为他是布置在Linux上的,纯数据类处理服务器,根本不可能用Qt写;这在Qt的http类中就表明了&…...
C++单例模式跨DLL调用问题梳理
问题案例: 假设有这样一个单例模式的代码 //test.h header class Test { public:static Test &instance() {static Test ins;return ins;}void foo(); };void testFoo();//test.cpp source #include "test.h"void Test::foo() {printf("%p\n&q…...
oracle闪回版本查询
闪回版本查询(Flashback Versions Query)是Oracle数据库提供的一种功能,允许用户查看某个表在特定时间范围内的所有版本。这对于审计和调试数据修改问题非常有用。通过闪回版本查询,你可以了解表中的数据在某个时间段内的变化历史…...
C#用winform窗口程序操作服务+不显示Form窗体,只显示右下角托盘图标+开机时自启动程序【附带项目地址】
服务的文章在:https://blog.csdn.net/weixin_43768573/article/details/144957941 一、用winform窗口程序操作服务 1、点击“创建新项目”,选择“Windows 服务(.NET Framework)” 2、给项目命名 3、右击项目->添加->新建项,选择“应用程序清单文件(仅限Windo…...
UOS系统和windows系统wps文档显示差异问题解决
最近在使用UOS系统的过程中,发现了一个很有意思的现象。就是在UOS系统上编辑的文档,发到windows系统上,会出现两个文档显示差异很大的情况,文档都是使用一样的wps软件打开的。到底是什么原因导致这种现象的呢?该如何解…...
JS中函数基础知识之查漏补缺(写给小白的学习笔记)
函数 函数是ECMAScript中 最有意思的部分之一, 主要是因为函数实际上是对象.-- 每个函数 都是Function类型的实例,Function也有属性和方法. 因为函数是对象,所以函数名就是指向函数对象的指针. 常用的定义函数的语法: ①函数声明 ②函数表达式 ③箭头函数 function sum (n…...
蓝桥杯训练
1对于一个字母矩阵,我们称矩阵中的一个递增序列是指在矩阵中找到两个字母,它们在同一行,同一列,或者在同一 45 度的斜线上,这两个字母从左向右看、或者从上向下看是递增的。 例如,如下矩阵中 LANN QIAO有…...
前端学习DAY33(外边距的折叠)
垂直外边距的重叠 在网页中相邻的垂直方向的外边距,会发生外边距的重叠 兄弟元素 兄弟元素之间的相邻外边距会取(绝对值)最大值,而不是取和,谁大取谁 特殊情况:如果相邻的外边距一正一负,则取两…...
asp.net core mvc的 ViewBag , ViewData , Module ,TempData
在 ASP.NET MVC 和 ASP.NET Core MVC 中,ViewBag 和 ViewData 是两种用于将数据从控制器传递到视图(View)的常用方法。它们都允许控制器将动态数据传递给视图,但它们的实现方式有所不同。关于 Module,它通常指的是某种…...
Linux驱动学习之第二个驱动程序(LED点亮关闭驱动程序-分层设计思想,使其能适应不同的板子-驱动程序模块为多个源文件怎么写Makefile)
目录 看这篇博文前请先掌握下面这些博文中的知识需要的PDF资料完整源代码board_fire_imx6ull-pro.c中的代码leddrv.c中的代码ledtest.c中的代码 程序设计思想和文件结构实现分层思想的具体方法概述具体实现分析定义结构体led_operations用来集合各个单板硬件层面操作LED的函数定…...
手写@EnableTransactionalManagement
定义一个注解,用于标注于方法上,标志着此方法是一个事务方法。 Target({ElementType.METHOD,ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) public interface MyTransaction {}定义一个开启事务功能的注解 Component Import(TransActionBean…...
【Vue】:解决动态更新 <video> 标签 src 属性后视频未刷新的问题
问题描述 在 Vue.js 项目,当尝试动态更新 <video> 标签的 <source> 元素 src 属性来切换视频时,遇到了一个问题:即使 src 属性已更改,浏览器仍显示旧视频。具体表现为用户选择新视频后,视频区域继续显示之…...
网络基础1 http1.0 1.1 http/2的演进史
http1.0 1.1 http/2的演进史😎 (连接复用 队头阻塞 服务器推送 2进制分帧) 概述 我们主要关注的是应用层 传输层 http协议发展历史 http的报文结构:起始行 Header Body http的典型特征 http存在的典型问题 Keep Alive机制 chun…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
