重温设计模式--单例模式
文章目录
- 单例模式(Singleton Pattern)概述
- 单例模式的实现方式及代码示例
- 1. 饿汉式单例(在程序启动时就创建实例)
- 2. 懒汉式单例(在第一次使用时才创建实例)
- 单例模式的注意事项
- 应用场景
- C++代码
- 懒汉模式-经典版(线程不安全)
- 经典版优化(线程安全)
- 内部静态变量的懒汉实现
- 饿汉模式
单例模式(Singleton Pattern)概述
-
定义:
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。就像是在整个软件系统中,某个特定的对象只能有一个,并且各个部分都能方便地获取到这个唯一的对象。 -
作用:
- 资源共享与协调:适用于管理一些全局的资源,比如数据库连接池。整个应用程序通常只需要一个数据库连接池实例来协调和管理数据库连接的分配与回收,避免创建多个连接池导致资源浪费和管理混乱。
- 状态一致性维护:在某些场景下,需要保证整个系统中某个对象的状态是唯一且一致的。例如,系统配置类,全局只有一份配置信息,各个模块获取的都是同一个配置实例,能保证配置的一致性,防止出现因多个不同配置实例而导致的逻辑混乱。
- 节省内存和避免重复创建:对于一些创建成本较高或者占用较多系统资源的对象,只创建一个实例可以避免多次重复创建带来的内存消耗和性能开销,像一些复杂的日志记录类,创建实例可能涉及到初始化大量的文件操作相关资源等,单例模式可保证只创建一次。
单例模式的实现方式及代码示例
1. 饿汉式单例(在程序启动时就创建实例)
#include <iostream>// 饿汉式单例类
class Singleton {
private:// 将构造函数声明为私有,防止外部创建实例Singleton() {std::cout << "创建单例实例" << std::endl;}// 静态成员变量保存唯一实例,在程序启动时就初始化static Singleton* instance;
public:// 获取单例实例的静态方法static Singleton* getInstance() {return instance;}
};// 静态成员变量初始化,在程序启动时就创建好实例
Singleton* Singleton::instance = new Singleton;
以下是使用饿汉式单例的示例代码:
int main() {Singleton* s1 = Singleton::getInstance();Singleton* s2 = Singleton::getInstance();// 比较两个指针,应该指向同一个实例if (s1 == s2) {std::cout << "s1和s2是同一个实例" << std::endl;}return 0;
}
在饿汉式单例中:
- 优点:实现简单,线程安全(因为在程序启动时就完成了实例的创建,不存在多个线程同时创建实例的竞争问题),在多线程环境下也能保证只有一个实例被创建。
- 缺点:如果单例类的构造函数执行一些比较耗时或者占用大量资源的初始化操作,并且这个单例可能在程序运行很久之后才会被用到,那么会造成程序启动时不必要的性能开销,提前占用了系统资源。
2. 懒汉式单例(在第一次使用时才创建实例)
#include <iostream>
#include <mutex>// 懒汉式单例类
class Singleton {
private:Singleton() {std::cout << "创建单例实例" << std::endl;}// 静态成员变量保存唯一实例指针static Singleton* instance;// 互斥锁用于保证多线程环境下的线程安全static std::mutex mutex_;
public:// 获取单例实例的静态方法,使用了双重检查锁定(DCLP)来优化线程安全和性能static Singleton* getInstance() {if (instance == nullptr) {std::lock_guard<std::mutex> guard(mutex_);if (instance == nullptr) {instance = new Singleton;}}return instance;}
};// 静态成员变量初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex_;
以下是使用懒汉式单例的示例代码:
int main() {Singleton* s1;Singleton* s2;// 模拟多线程环境下获取单例实例std::thread t1([&]() { s1 = Singleton::getInstance(); });std::thread t2([&]() { s2 = Singleton::getInstance(); });t1.join();t2.join();if (s1 == s2) {std::cout << "s1和s2是同一个实例" << std::endl;}return 0;
}
在懒汉式单例中:
- 优点:实例在第一次使用时才创建,避免了程序启动时不必要的资源占用和性能开销,对于那些创建成本较高且可能不会马上用到的单例对象比较合适。
- 缺点:实现相对复杂一些,需要考虑多线程环境下的线程安全问题,虽然使用了双重检查锁定等优化手段,但如果处理不当还是可能出现问题(比如内存乱序执行等情况,不过现代编译器和处理器一般会有相应机制来尽量避免)。
单例模式的注意事项
- 构造函数私有:无论是饿汉式还是懒汉式,都要将构造函数声明为私有,这样可以防止外部代码通过常规的方式(如
Singleton s;
这种直接实例化的语句)来创建多个实例,保证了单例的唯一性。 - 线程安全问题:在多线程环境下,懒汉式单例需要特别注意线程安全,要采用合适的同步机制(如互斥锁、原子操作等)来确保在多个线程同时尝试获取实例时,只有一个线程能够创建实例,避免创建出多个实例破坏单例模式的规则。而饿汉式单例天然具有一定的线程安全性,但也需要根据具体应用场景来考虑是否满足需求。
- 对象生命周期管理:要注意单例对象的生命周期,尤其是在动态内存分配(如
new
操作符创建实例)的情况下,需要合理地处理实例的释放,避免内存泄漏等问题。例如,可以通过定义一个静态的析构函数来释放单例对象占用的资源,但这需要谨慎设计,防止出现意外的行为。
单例模式在很多软件系统中都有广泛应用,不过也要根据实际情况合理选择合适的实现方式和注意相关的设计要点,以确保其能正确地发挥作用。
应用场景
- 系统配置管理:在一个应用程序中,通常会有各种配置信息,如数据库连接配置、服务器端口配置、应用程序的一些全局参数等。将这些配置信息封装在一个单例的配置类中,整个系统通过唯一的实例来获取和修改配置,保证了配置的一致性,并且方便统一管理。
- 日志记录器:用于记录程序运行过程中的各种日志信息,整个程序往往只需要一个日志记录实例来将日志输出到文件、控制台或者发送到远程日志服务器等。不同的模块都向这个唯一的日志记录器实例写入日志,确保日志管理的统一性和有序性。
- 线程池管理:在多线程编程中,线程池是管理和复用线程资源的重要组件。一般一个应用程序只需要一个线程池实例,通过这个单例的线程池来分配线程执行任务、回收线程等,避免创建多个线程池导致资源浪费和线程调度混乱。
- 缓存机制:例如网页缓存、数据库查询结果缓存等场景。以网页缓存为例,一个网站服务器可以有一个单例的缓存类,用于存储经常访问的网页内容,下次再有相同请求时可以直接从缓存中获取,减少服务器的响应时间和数据库等资源的消耗,而且只有一个缓存实例方便管理缓存的有效性、容量控制等。
C++代码
懒汉模式-经典版(线程不安全)
#include <iostream>
using namespace std;//懒汉模式
class Singleton
{
public:
/**
*需要提供要给全局访问点,就需要在类中定义一个static函数,返回在类内部唯一构造的实例
*/static Singleton *GetInstance(){if (m_Instance == NULL ){m_Instance = new Singleton ();}return m_Instance;}static void DestoryInstance(){if (m_Instance != NULL ){delete m_Instance;m_Instance = NULL ;}}private:
/**
*构造函数卸载私有里,为了防止在外部调用类的构造函数而构造实例
*/Singleton();static Singleton *m_Instance;
};Singleton *Singleton ::m_Instance = NULL;int main(int argc , char *argv [])
{Singleton *singletonObj = Singleton ::GetInstance(); Singleton ::DestoryInstance();return 0;
}
经典版优化(线程安全)
#include <iostream>
using namespace std;//懒汉模式
class Singleton
{
public:
/*
此处进行了两次m_Instance == NULL的判断,是借鉴了Java的单例模式实现时,使用的所谓的“双检锁”机制。
因为进行一次加锁和解锁是需要付出对应的代价的,而进行两次判断,就可以避免多次加锁与解锁操作,同时也
保证了线程安全。但是,这种实现方法在平时的项目开发中用的很好,也没有什么问题?但是,如果进行大数据
的操作,加锁操作将成为一个性能的瓶颈;为此,一种新的单例模式的实现也就出现了。
*/static Singleton *GetInstance(){if (m_Instance == NULL ){Lock(); if (m_Instance == NULL ){m_Instance = new Singleton ();}UnLock(); }return m_Instance;}static void DestoryInstance(){if (m_Instance != NULL ){delete m_Instance;m_Instance = NULL ;}}
private:Singleton();static Singleton *m_Instance;
};Singleton *Singleton ::m_Instance = NULL;int main(int argc , char *argv [])
{Singleton *singletonObj = Singleton ::GetInstance();Singleton ::DestoryInstance();return 0;
}
内部静态变量的懒汉实现
此方法也很容易实现,在instance函数里定义一个静态的实例,也可以保证拥有唯一实例,在返回时只需要返回其指针就可以了。推荐这种实现方法,真得非常简单。
#include <iostream>
using namespace std;class Singleton
{
public:static Singleton *GetInstance(){lock();static Singleton m_Instance;unlock();return &m_Instance;}
private:Singleton();};int main(int argc , char *argv [])
{Singleton *singletonObj = Singleton ::GetInstance();cout<<singletonObj->GetTest()<<endl;singletonObj = Singleton ::GetInstance();cout<<singletonObj->GetTest()<<endl;
}
饿汉模式
#include <iostream>
using namespace std;class Singleton
{
public:static Singleton *GetInstance(){return m_instace;}
private:Singleton();static Singleton *m_instance;};
Singleton* Singleton :: m_instance = new Singleton();
int main(int argc , char *argv [])
{Singleton *singletonObj = Singleton ::GetInstance();singletonObj = Singleton ::GetInstance();return 0;
}
相关文章:

重温设计模式--单例模式
文章目录 单例模式(Singleton Pattern)概述单例模式的实现方式及代码示例1. 饿汉式单例(在程序启动时就创建实例)2. 懒汉式单例(在第一次使用时才创建实例) 单例模式的注意事项应用场景 C代码懒汉模式-经典…...
【人工智能】Python中的机器学习管道:如何用scikit-learn构建高效的ML管道
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在机器学习项目中,数据预处理、特征工程、模型训练与评估是不可或缺的环节。随着项目规模的扩大和复杂度的增加,手动管理这些步骤不仅繁琐…...

Redis存在安全漏洞
Redis是美国Redis公司的一套开源的使用ANSI C编写、支持网络、可基于内存亦可持久化的日志型、键值(Key-Value)存储数据库,并提供多种语言的API。 Redis存在安全漏洞。攻击者利用该漏洞使用特制的Lua脚本触发堆栈缓冲区溢出漏洞,从…...

Scala图书管理系统
项目创建并实现基础UI package org.appimport scala.io.StdInobject Main {def main(args: Array[String]): Unit {var running truewhile (running) {println("欢迎来到我的图书管理系统,请选择")println("1.查看所有图书")println("2…...

【数据可视化案列】白葡萄酒质量数据的EDA可视化分析
🧑 博主简介:曾任某智慧城市类企业算法总监,目前在美国市场的物流公司从事高级算法工程师一职,深耕人工智能领域,精通python数据挖掘、可视化、机器学习等,发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…...

Postman接口测试:全局变量/接口关联/加密/解密
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 全局变量和环境变量 全局变量:在postman全局生效的变量,全局唯一 环境变量:在特定环境下生效的变量,本环境内唯一 …...

vue+elementui实现下拉表格多选+搜索+分页+回显+全选2.0
一、vueelementui实现下拉表格多选搜索1.0 二、vueelementui实现下拉表格多选搜索分页回显全选2.0 在1.0的基础上,终于可以实现在下拉框表格分页的前提下不同页码的回显辣,分页是前端来分页的(代码略乱且没有封装还很长,随便看看…...
电商系统-产品经理
电视产品经理的工作体系: 产品经理的分类与职责 C端产品经理:面向个人用户,关注用户体验和产品易用性B端产品经理:面向企业客户,注重功能完整性和商业价值专业方向细分: 用户产品经理:专注用户…...

《庐山派从入门到...》PWM板载蜂鸣器
《庐山派从入门到...》PWM板载蜂鸣器 配置PWM模块控制板载无源蜂鸣器播放【一闪一闪亮晶晶】播放do re mi 《庐山派从入门到...》PWM控制无源蜂鸣器 PWM(Pulse Width Modulation,脉宽调制)是一种在嵌入式系统中常用的技术,它可以用…...

【河南新标】豫财预〔2024〕105号-《关于省级政务信息化建设项目支出预算标准的规定》-费用标准解读系列29
2024年12月3日,河南省财政厅发布了《关于省级政务信息化建设项目支出预算标准的规定》豫财预〔2024〕105号。《关于省级政务信息化建设项目支出预算标准的规定 (试行)》(豫财预 〔2020〕81号)同时废止。新的豫财预〔20…...

【数据结构】数据结构整体大纲
数据结构用来干什么的?很简单,存数据用的。 (这篇文章仅介绍数据结构的大纲,详细讲解放在后面的每一个章节中,逐个击破) 那为什么不直接使用数组、集合来存储呢 ——> 如果有成千上亿条数据呢ÿ…...

【C++基础】09、结构体
一、结构体(struct) C/C 数组允许定义可存储相同类型数据项的变量,但是结构体是 C 中另一种用户自定义的可用的数据类型,它允许存储不同类型的数据项。 结构体用于表示一条记录,假设现在想要跟踪图书馆中书本的动态,可能需要跟踪每…...

大恒相机开发(2)—Python软触发调用采集图像
大恒相机开发(2)—Python软触发调用采集图像 完整代码详细解读和功能说明扩展学习 这段代码是一个Python程序,用于从大恒相机采集图像,通过软件触发来采集图像。 完整代码 咱们直接上python的完整代码: # version:…...

QT的前景与互联网岗位发展
qt是用来干什么的 --》桌面应用开发(做电脑的应用程序,面对客户端)。 主要用于开发跨平台的应用程序和用户界面(UI)。它是一个全面的C库集合,提供了构建软件应用所需的各种工具和功能。 客户端开发的重…...

青藤入选工信部“2024年网络安全技术应用典型案例拟支持项目名单”
近日,工业和信息化部公示了“2024年网络安全技术应用典型案例拟支持项目名单”,青藤云安全联合某股份制银行共同申报的主机威胁狩猎平台项目凭借其技术先进性及行业示范性成功入选。 网络安全技术应用试点示范工作是由工业和信息化部、国家互联网信息办公…...
NVIDIA GPU 内部架构介绍
NVIDIA GPU 架构 NVIDIA GPU 的 SM(Streaming Multiprocessor) 和 GPC(Graphics Processing Cluster) 是 GPU 架构中的关键组成部分。它们决定了 GPU 的计算能力和性能,以下是对这两个参数的详细介绍: 1. …...

重温设计模式----装饰模式
文章目录 装饰模式定义UML 图其主要优点包括:装饰模式的主要角色有:C 代码示例总结 装饰模式定义 动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式必生成子类更加灵活 装饰模式(Decorator Pattern&…...

第十六章 C++ 字符串
C 字符串 C 提供了以下两种类型的字符串表示形式: C 风格字符串C 引入的 string 类类型 C 风格字符串 C 风格的字符串起源于 C 语言,并在 C 中继续得到支持。字符串实际上是使用 null 字符 终止的一维字符数组。因此,一个以 null 结尾的…...
MySQL中Seconds_Behind_Master是怎么计算的
目录 1.Seconds_Behind_Master计算方式2.Seconds_Behind_Master 计算方式会存在什么问题3.更好的方式3.1 实现方法3.2 优点在MySQL中,Seconds_Behind_Master是一个用于表示从库(Slave)落后于主库(Master)的时间(以秒为单位)的指标。 1.Seconds_Behind_Master计算方式 其…...
React Native 集成原生Android功能
React Native 集成原生功能完整指南 前言 在 React Native 开发中,我们经常需要使用设备的原生功能,比如蓝牙、打印机等。本文将以集成打印机功能为例,详细介绍如何在 React Native 项目中集成 Android 原生功能。 集成步骤概述 创建原生…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...

从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...

iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...