当前位置: 首页 > news >正文

C++:设计模式-单例模式

单例模式(Singleton Pattern)是一种设计模式,确保一个类只有一个实例,并且提供全局访问点。实现单例模式的关键是防止类被多次实例化,且能够保证实例的唯一性。常见的实现手法包括懒汉式饿汉式线程安全的懒汉式等。

1. 饿汉式(Eager Initialization)

饿汉式单例在程序启动时就创建实例,并且保证只有一个实例。适用于单例实例比较简单、没有资源消耗问题的情况。

class Singleton {
public:// 提供静态的访问方式static Singleton& getInstance() {return instance;  // 直接返回静态实例}private:Singleton() {}  // 构造函数私有化,防止外部创建实例~Singleton() {}Singleton(const Singleton&) = delete;  // 禁止拷贝构造函数Singleton& operator=(const Singleton&) = delete;  // 禁止赋值static Singleton instance;  // 静态实例,程序启动时创建
};// 静态实例必须定义在类外
Singleton Singleton::instance;

注意
Singleton Singleton::instance;应在类的实现文件(.cpp 文件)中定义。如果静态成员变量的定义(如 Singleton Singleton::instance;)也放在头文件中,由于头文件可能被多个源文件包含,就会导致多重定义错误。

优点

  • 简单直接,保证了类的实例唯一。
  • 实例在程序启动时就被创建,不会受到多线程的影响。

缺点

  • 无法延迟实例化。即使单例没有被使用,实例也会在程序启动时创建,可能会浪费资源。

在饿汉式单例中,实例在程序启动时就被创建,因此你无需显式地调用 getInstance() 来创建对象。它是静态的,并且一开始就存在。你只能通过 getInstance() 方法来访问该实例。

以下是如何在代码中调用饿汉式单例的示例:

完整代码示例
#include <iostream>
using namespace std;class Singleton {
public:// 提供静态的访问方式static Singleton& getInstance() {return instance;  // 直接返回静态实例}void showMessage() {cout << "Singleton instance is working!" << endl;}private:Singleton() { cout << "Singleton Constructor Called!" << endl; }  // 构造函数私有化~Singleton() { cout << "Singleton Destructor Called!" << endl; }Singleton(const Singleton&) = delete;  // 禁止拷贝构造函数Singleton& operator=(const Singleton&) = delete;  // 禁止赋值static Singleton instance;  // 静态实例,程序启动时创建
};// 静态实例必须定义在类外
Singleton Singleton::instance;int main() {// 通过调用 getInstance 来获取单例实例Singleton& s1 = Singleton::getInstance();s1.showMessage();// 由于我们不能复制实例,以下代码会编译错误:// Singleton s2 = Singleton::getInstance();  // 编译错误,不能复制单例// 单例是全局唯一的,只能通过 getInstance() 获取return 0;
}
代码解析:
  1. 静态实例定义

    • static Singleton instance; 在类内部声明了一个静态的实例,该实例在程序启动时被创建。
    • 静态变量 instance 的生命周期从程序开始直到程序结束,所以它是全局唯一的。
  2. getInstance() 方法

    • static Singleton& getInstance() 提供了一个全局的访问点,用来获取唯一的实例。这个方法返回一个对静态成员 instance 的引用。
  3. main() 中的使用

    • main() 函数中,我们通过 Singleton::getInstance() 获取了单例实例,并调用了实例的方法 showMessage()
    • 单例实例的构造函数在第一次调用 getInstance() 时自动执行,但我们并没有显式地创建 Singleton 对象。
输出:
Singleton Constructor Called!
Singleton instance is working!
注意事项:
  • 全局唯一Singleton::getInstance() 返回的是同一个对象,所以每次调用 getInstance() 都会得到相同的实例。
  • 构造函数:构造函数只在第一次调用 getInstance() 时执行一次,因此 Singleton Constructor Called! 只会打印一次。
  • 禁止复制:拷贝构造函数和赋值运算符被删除,避免了实例的复制。编译时如果尝试复制单例实例(如 Singleton s2 = Singleton::getInstance();),会导致编译错误。
总结:

通过 Singleton::getInstance() 方法获取单例实例,这是调用饿汉式单例的标准方式。由于饿汉式实例在程序启动时就被创建,所以你不需要显式地进行实例化操作。


2. 懒汉式(Lazy Initialization)

懒汉式是在需要实例时才创建实例,这样可以延迟实例化,提高资源的使用效率。基本实现没有线程安全,多个线程同时访问时可能会出现问题。

class Singleton {
public:static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton();}return instance;}private:Singleton() {}  // 构造函数私有化~Singleton() {}Singleton(const Singleton&) = delete;  // 禁止拷贝构造函数Singleton& operator=(const Singleton&) = delete;  // 禁止赋值static Singleton* instance;
};// 静态实例初始化为空指针
Singleton* Singleton::instance = nullptr;

优点

  • 实例化延迟,只有在第一次使用时才创建实例,避免了不必要的资源消耗。

缺点

  • 不是线程安全的。在多线程环境下,可能会创建多个实例。

3. 线程安全的懒汉式

为了确保线程安全,可以使用互斥锁(mutex)来同步访问,确保只有一个线程能够创建实例。

#include <mutex>class Singleton {
public:static Singleton* getInstance() {if (instance == nullptr) {std::lock_guard<std::mutex> lock(mutex);  // 加锁确保线程安全if (instance == nullptr) {instance = new Singleton();}}return instance;}private:Singleton() {}  // 构造函数私有化~Singleton() {}Singleton(const Singleton&) = delete;  // 禁止拷贝构造函数Singleton& operator=(const Singleton&) = delete;  // 禁止赋值static Singleton* instance;static std::mutex mutex;  // 互斥锁
};// 静态实例初始化为空指针
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;

优点

  • 线程安全,保证在多线程环境下只有一个实例被创建。

缺点

  • 使用互斥锁增加了性能开销,尤其是频繁访问 getInstance() 时。

4. 双重检查锁(Double-Checked Locking)

双重检查锁定是懒汉式的优化版本,它减少了锁的使用频率。在第一次检查时不加锁,只有当实例为 nullptr 时才加锁,这样可以避免每次调用 getInstance() 时都进行加锁操作。

#include <mutex>class Singleton {
public:static Singleton* getInstance() {if (instance == nullptr) {std::lock_guard<std::mutex> lock(mutex);  // 加锁if (instance == nullptr) {  // 双重检查instance = new Singleton();}}return instance;}private:Singleton() {}  // 构造函数私有化~Singleton() {}Singleton(const Singleton&) = delete;  // 禁止拷贝构造函数Singleton& operator=(const Singleton&) = delete;  // 禁止赋值static Singleton* instance;static std::mutex mutex;  // 互斥锁
};// 静态实例初始化为空指针
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;

优点

  • 提供了线程安全的懒汉式实现,且避免了每次都加锁的性能开销。

缺点

  • 代码相对复杂,需要小心实现。对 instance 的访问需要特别注意线程间的同步。

5. 静态局部变量(最推荐的实现方式)

使用静态局部变量来实现单例模式,这种方式是线程安全的,并且实现简单。C++11标准及以上可以确保静态局部变量的初始化是线程安全的。

class Singleton {
public:static Singleton& getInstance() {static Singleton instance;  // 静态局部变量return instance;}private:Singleton() {}  // 构造函数私有化~Singleton() {}Singleton(const Singleton&) = delete;  // 禁止拷贝构造函数Singleton& operator=(const Singleton&) = delete;  // 禁止赋值
};

优点

  • 简洁、线程安全。
  • 在程序启动时不会立即创建实例,而是等到第一次使用时创建。
  • 不需要显式加锁。

缺点

  • 只适用于需要在第一次使用时实例化的情况。

总结

  • 饿汉式:简单,适合不需要延迟初始化的场景,程序启动时就创建实例。
  • 懒汉式:适合需要延迟初始化的场景,但需要考虑线程安全。
  • 线程安全懒汉式:通过加锁保证线程安全,但可能带来性能开销。
  • 双重检查锁:线程安全,减少了加锁的频率,但实现复杂。
  • 静态局部变量:线程安全,简洁,现代 C++ 中推荐的单例实现方式。

在现代 C++ 中,静态局部变量是实现单例模式的首选方法,既保证了线程安全,又没有性能开销,是最优选择。

相关文章:

C++:设计模式-单例模式

单例模式&#xff08;Singleton Pattern&#xff09;是一种设计模式&#xff0c;确保一个类只有一个实例&#xff0c;并且提供全局访问点。实现单例模式的关键是防止类被多次实例化&#xff0c;且能够保证实例的唯一性。常见的实现手法包括懒汉式、饿汉式、线程安全的懒汉式等。…...

Softing工业将OPC UA信息建模集成到边缘应用和安全集成服务器中

Softing工业宣布将OPC UA&#xff08;统一架构&#xff09;信息建模集成到其边缘产品系列及安全集成服务器&#xff08;SIS&#xff09;中&#xff0c;这一技术进步使得在工业物联网&#xff08;IIoT&#xff09;应用中的数据集成、交换与控制更加无缝、有效。 &#xff08;OPC…...

WPF中如何让Textbox显示为一条直线

由于Textbox直接使用是一条直线 设置如下代码 可以让Textbox变为直线输入 <Style TargetType"TextBox"x:Key"UsernameTextBoxStyle"><Setter Property"Template"><Setter.Value><ControlTemplate TargetType"{x:Typ…...

VSCode汉化教程【简洁易懂】

我们安装完成后默认是英文界面。 找到插件选项卡&#xff0c;搜索“Chinese”&#xff0c;找到简体&#xff08;更具你的需要&#xff09;&#xff08;Microsoft提供&#xff09;Install。 安装完成后选择Change Language and Restart。...

跨平台多开账号防关联:轻松管理多个账号!

对于跨境电商、独立站以及社媒营销领域&#xff0c;如何高效管理多个账号、确保账号安全是企业面临的重大挑战。那么如何仅用一台电脑就能实现跨平台多开账号呢&#xff1f; 一、为什么需要跨平台多开账号并防关联&#xff1f; 1. 品牌推广&#xff1a;不同平台拥有不同的用户…...

DICOM图像处理:深入解析DICOM彩色图像中的Planar配置及其对像素数据解析处理的实现

引言 在DICOM(Digital Imaging and Communications in Medicine)标准中,彩色图像的存储与显示涉及多个关键属性,其中**Planar Configuration(平面配置)**属性(标签 (0028,0006))尤为重要。当遇到彩色DICOM图像在浏览时被错误地分割为9张小图,而实际应显示为一…...

jupyter notebook的 markdown相关技巧

目录 1 先选择为markdown类型 2 开关技巧 2.1 运行markdown 2.2 退出markdown显示效果 2.3 注意点&#xff1a;一定要 先选择为markdown类型 3 一些设置技巧 3.1 数学公式 3.2 制表 3.3 目录和列表 3.4 设置各种字体效果&#xff1a;加粗&#xff0c;斜体&#x…...

Linux连接网络的三种方式

Linux 连接网络的三种常见方式如下&#xff1a; 桥接模式 原理&#xff1a;虚拟网络接口与物理网络接口或另一个虚拟接口 “桥接”&#xff0c;形成逻辑上的网络交换机&#xff0c;使所有通过该桥接设备的数据包能被转发到桥接组中的所有接口&#xff0c;如同在一个局域网内…...

##继承##

继承的概念 #继承是新模板基于老模板的基础上修改而成&#xff0c;制作新模板时不需要重新开始制作&#xff0c;可以在老模板的基础上进行修改.(如手机版本的换代&#xff0c;软件的版本更新等) #程序也可以继承 继承的格式: class 继承模块&#xff08;被继承模块&#xff…...

2024 APMCM亚太数学建模C题 - 宠物行业及相关产业的发展分析和策略 完整参考论文(1)

摘要 近年来,中国宠物食品行业迅速增长,但面临复杂的国际形势和多变的市场环境,因此科学地分析和预测该行业的发展趋势至关重要。本研究通过构建多个机器学习与统计回归模型,量化分析中国宠物食品行业的关键驱动因素,预测未来宠物食品总产值和出口值。 在数据处理部分,…...

uni-app 修改复选框checkbox选中后背景和字体颜色

编写css&#xff08;注意&#xff1a;这个样式必须写在App.vue里&#xff09; /* 复选框 */ /* 复选框-圆角 */ checkbox.checkbox-round .wx-checkbox-input, checkbox.checkbox-round .uni-checkbox-input {border-radius: 100rpx; } /* 复选框-背景颜色 */ checkbox.checkb…...

使用Notepad++工具去除重复行

使用Notepad工具去除重复行 参考链接&#xff1a;https://blog.csdn.net/londa/article/details/108981396 一 、使用正则表达式 1、对文本进行排序&#xff0c;让重复行排在一起 2、使用正则表达式替换&#xff08;注意&#xff09;^(.*?)$\s?^(?.*^\1$) 在替换时选择正…...

基于Springboot+Vue的救灾物资调动系统 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 这个系…...

Unity 使用 Excel 进行配置管理(读Excel配置表、Excel转保存Txt 文本、读取保存的 Txt 文本配置内容)

Unity 使用 Excel 进行配置管理(读Excel配置表、Excel转保存Txt 文本、读取保存的 Txt 文本配置内容) 目录 Unity 使用 Excel 进行配置管理(读Excel配置表、Excel转保存Txt 文本、读取保存的 Txt 文本配置内容) 一、简单介绍 二、实现原理 三、注意事项 四、案例简单步…...

MySQL中索引全详解

第一部分&#xff1a;什么是索引 索引在数据库中就像书的目录&#xff0c;能够快速定位数据位置&#xff0c;从而提升查询效率。没有索引时&#xff0c;数据库查询需要从头到尾扫描整个表&#xff08;称为全表扫描&#xff09;&#xff0c;这在数据量大时非常耗时。有了索引后&…...

vllm serve的参数大全及其解释

以下是 vllm serve 的常见参数说明以及它们的作用&#xff1a; 1. 基本参数 model_tag 说明&#xff1a;用于指定要加载的模型&#xff0c;可以是 Hugging Face 模型仓库中的模型名称&#xff0c;也可以是本地路径。示例&#xff1a;vllm serve "gpt-neo-2.7B"--co…...

2025职业院校技能大赛信息安全管理与评估(河北省) 任务书

2025职业院校技能大赛信息安全管理与评估--河北省 任务书 模块一网络平台搭建与设备安全防护任务1&#xff1a;网络平台搭建 &#xff08;50分&#xff09;任务2&#xff1a;网络安全设备配置与防护&#xff08;250分&#xff09; 模块二网络安全事件响应、数字取证调查、应用程…...

通过高德 JS API 实现H5端定位

实现步骤: 1、安装 amap-jsapi-loader 插件 npm install amap-jsapi-loader 2、对定位组件进行封装 gb-location组件 <script lang="ts" setup> import AMapLoader from @amap/amap-jsapi-loader; import {ref,defineExpose} from vue;let map = ref(nul…...

第J6周:RenseNeXt-50实战

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 文章目录 一、前言1、结构改进2、分组卷积 二、前期工作1.设置GPU2. 导入数据3. 查看数据 三、数据预处理1、加载数据2、配置数据集 四、构建网络1、导入包2、…...

JAVA八股与代码实践----接口与抽象类的区别和用法

接口和抽象类的区别 关键字abstractinterface 实例化不能直接实例化不能直接实例化 方法可以有抽象和具体方法只能有抽象方法&#xff08;Java 8 支持默认方法&#xff09; 变量可以有普通变量只能有常量 (public static final) 继承单继承多继承 构造函数可以定义不允许…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...