单例模式-如何保证全局唯一性?
以下是几种实现单例模式并保证全局唯一性的方法:
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…...
go的工具库:github.com/expr-lang/expr
github.com/expr-lang/expr 是一个 Go 语言的表达式求值库,它允许你在运行时安全地执行表达式。主要用途包括: 1.表达式求值: program, err : expr.Compile("2 2") if err ! nil {// 处理错误 } result, err : expr.Run(program…...
MCP通信方式之Streamable HTTP
目录 一、前言二、三种传输方式对比1、Stdio和 HTTP SSE工作原理2、Streamable HTTP3、Streamable HTTP解决什么问题三、Streamable HTTP MCP设计原理四、Streamable HTTP MCP demo演示1、MCP server示例2、MCP Client示例一、前言 2025年5月9日,MCP(Model Context Protocol)…...

大数据Spark(六十一):Spark基于Standalone提交任务流程
文章目录 Spark基于Standalone提交任务流程 一、Standalone-Client模式 1、提交命令 2、任务执行流程 二、Standalone-Cluster模式 1、提交命令 2、任务执行流程 Spark基于Standalone提交任务流程 在Standalone模式下,Spark的任务提交根据Driver程序运行的位…...
for AC500 PLCs 3ADR025003M9903的安全说明
1安全说明 必须遵守特殊的环境条件(例如,由于爆炸性物质、重污染或腐蚀影响的危险区域)。必须在指定的技术数据和系统数据范围内处理和操作设备。该装置不含可维修部件,不得打开。除非另有规定,否则操作过程中必须关闭可拆卸的盖子。拒绝对不…...
NoSQL 之 Redis 配置与优化
目录 一、 前置知识点 1. 关系数据库与非关系型数据库 (1)关系型数据库 (2)非关系型数据库 (3)非关系型数据库产生背景 (4)两者对比 2. Redis 基础 (1࿰…...
Redis 与 MySQL 数据一致性保障方案
在高并发场景下,Redis 作为缓存中间件与 MySQL 数据库配合使用时,数据一致性是一个关键挑战。本文将详细探讨如何保障 Redis 与 MySQL 的数据一致性,并结合 Java 代码实现具体方案。 数据不一致的原因分析 在分布式系统中,Redis…...

基于STM32的DHT11温湿度远程监测LCD1602显示Proteus仿真+程序+设计报告+讲解视频
DHT11温湿度远程监测proteus仿真 1. 主要功能2.仿真3. 程序4. 设计报告5. 资料清单&下载链接 基于STM32的DHT11温湿度远程监测LCD1602显示Proteus仿真设计(仿真程序设计报告讲解视频) 仿真图proteus 8.9 程序编译器:keil 5 编程语言:C…...

CSS 定位:原理 + 场景 + 示例全解析
一. 什么是CSS定位? CSS中的position属性用于设置元素的定位方式,它决定了元素在页面中的"定位行为" 为什么需要定位? 常规布局(如 display: block)适用于主结构 定位适用于浮动按钮,弹出层,粘性标题等场景帮助我们精确控制元素在页面中的位置 二. 定位类型全…...
VSCode主题定制:CSS个性化你的编程世界
在今天的数字世界,编程环境已成为开发者的第二大脑,而主题正是个性化你的创意空间的关键。本文将指导你如何使用CSS自定义VSCode的主题,让你的IDE不仅功能强大,更具视觉个性。 思路分析 设计思路: 创建主色调基调和…...
springboot的test模块使用Autowired注入失败
springboot的test模块使用Autowired注入失败的原因: 注入失败的原因可能是用了junit4的包的Test注解 import org.junit.Test;解决方法:再加上RunWith(SpringRunner.class)注解即可 或者把Test由junit4改成junit5的注解,就不用加上RunWith&…...