单例模式-如何保证全局唯一性?
以下是几种实现单例模式并保证全局唯一性的方法:
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…...
【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
goreplay
1.github地址 https://github.com/buger/goreplay 2.简单介绍 GoReplay 是一个开源的网络监控工具,可以记录用户的实时流量并将其用于镜像、负载测试、监控和详细分析。 3.出现背景 随着应用程序的增长,测试它所需的工作量也会呈指数级增长。GoRepl…...
大模型——基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程
基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程 下载安装Docker Docker官网:https://www.docker.com/ 自定义Docker安装路径 Docker默认安装在C盘,大小大概2.9G,做这行最忌讳的就是安装软件全装C盘,所以我调整了下安装路径。 新建安装目录:E:\MyS…...
