重温设计模式--单例模式
文章目录
- 单例模式(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 原生功能。 集成步骤概述 创建原生…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...
Android写一个捕获全局异常的工具类
项目开发和实际运行过程中难免会遇到异常发生,系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler,它是Thread的子类(就是package java.lang;里线程的Thread)。本文将利用它将设备信息、报错信息以及错误的发生时间都…...
