C++设计模式——Singleton单例模式

一、单例模式的定义  

单例模式,英文全称Singleton Pattern,是一种创建型设计模式,它保证一个类在程序中仅有一个实例,并对外提供一个访问的该类实例的全局接口。

单例模式通常用于需要控制对象资源的开发场景,一个类只创建一个对象的设计,既可以避免创建过多副本所造成的资源浪费现象,又可以避免引发数据一致性等问题。

在数据库连接、线程池设计、日志系统设计等开发场景,经常使用单例模式来创建对象,可以有效地降低对内存资源的占用。

在编码时,为了防止外部直接通过new关键字创建对象实例,Singleton类的构造函数必须是私有的。

在多线程开发场景,单例模式可以避免多个线程同时访问同一个资源,从而避免资源竞争的问题,如果还需要进一步保证线程安全性,可以在创建实例时添加同步锁。

单例模式在现实生活中的抽象实例:

电力公司:在一个城市或地区,通常只有一个电力公司负责供电,我们可以通过该公司来获取电力服务。

总统办公室:在一个国家,通常只有一个总统办公室负责国家事务处理,办公室负责确保国家事务的一致性和高效运作。

登录系统:用户只需要登录一次就可以打开多个应用程序的界面。

二、单例模式的结构

单例模式主要包含以下组件:

1.Singleton类:这是一个单例类,它在整个系统中只有一个实例。通常使用私有构造函数来创建、使用公共静态成员方法来访问。

2.私有构造函数:为了避免被外部实例化,Singleton类通常会定义一个私有构造函数,使得其他类无法通过调用构造函数创建Singleton的实例对象。

3.私有静态成员变量:Singleton类通常会声明一个私有静态成员变量,用来保存Singleton的唯一实例,且这个静态变量只能在Singleton类内部访问。

4.公共的对外接口:它通常被命名为getInstance(),通过该接口可以获取Singleton的唯一实例对象。该接口使用懒汉/饿汉的方式来实现,在接口内部会先判断实例是否已经存在,如果存在则直接返回,否则创建一个新的实例并返回。

组件之间的工作步骤如下:

1.饿汉模式的程序启动:在程序启动的同时就创建好Singleton实例,并提供全局唯一的外部访问接口,外部程序可以直接调用该接口来获取Singleton实例。

2.懒汉模式的程序启动:程序启动时并不会创建Singleton实例,程序在等到单例对象被第一次使用时才创建Singleton实例。

3.采用私有的静态变量存储已经创建好的Singleton实例。

4.外部客户端通过唯一的外部访问接口来访问并使用Singleton实例。

饿汉模式单例 & 懒汉模式单例:

饿汉模式(Singleton with Instantiation):

是一种线程安全的实现方法,在类初始化时就完成了单例实例的创建。

懒汉模式(Singleton with Lazy Initialization):

也被称为"双检锁"模式,只有当第一次真正需要获取单例实例时才进行单例实例的创建。

饿汉模式和懒汉模式都实现了单例,即保证在整个应用程序的生命周期中只有一个Singleton类实例。

饿汉模式的优点是线程安全,但缺点是如果该实例很复杂会增加初始化的耗时,从而导致程序的启动时间被延长。

懒汉模式的优点是延迟加载,可以节约资源和减少程序的启动耗时,缺点是需要考虑多线程环境下创建对象导致的线程安全问题,它通常在外部访问接口中使用双重检查锁定(Double-checked locking)来保证线程安全。

对应UML类图:

三、单例模式代码样例

1.单例模式的伪代码:

#include <iostream>class Singleton {
private:static Singleton* instance;//私有构造函数,确保不能从外部实例化对象Singleton() {}public://获取单例实例的静态方法static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton();}return instance;}
};Singleton* Singleton::instance = nullptr;int main() {Singleton* singleton = Singleton::getInstance();return 0;
}

2.单例模式的加锁版伪代码:

class Singleton {
private:static Singleton* instance;Singleton() {} //私有化构造函数,防止外部创建实例public:static Singleton* getInstance() {if (instance == nullptr) {//在多线程环境下需要加锁保证只创建一个实例// std::lock_guard<std::mutex> lock(mutex);instance = new Singleton();}return instance;}//单例类的其他成员函数void doSomething() {// ...}
};Singleton* Singleton::instance = nullptr;int main() {Singleton* obj1 = Singleton::getInstance();obj1->doSomething();Singleton* obj2 = Singleton::getInstance();obj2->doSomething();delete obj1; //释放资源return 0;
}

3.饿汉模式的伪代码:

class Singleton {
private:static Singleton* instance;//私有构造函数,防止外部直接创建对象Singleton() {}public://静态成员函数,用于获取单例对象static Singleton* getInstance() {return instance;}
};//提前创建好单例对象
Singleton* Singleton::instance = new Singleton();

4.懒汉模式的伪代码:

class Singleton {
private:static Singleton* instance;Singleton() {}public://使用时才创建单例对象static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton();}return instance;}
};

Demo1:完整代码实现

#include <iostream>class Singleton {
private:static Singleton* instance;Singleton() {}public:static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton();}return instance;}void showMessage() {std::cout << "Hello, World!" << std::endl;}
};Singleton* Singleton::instance = nullptr;int main() {// 获取Singleton对象实例Singleton* singleton = Singleton::getInstance();// 调用对象的方法singleton->showMessage();return 0;
}

运行结果:

Hello, World!

Demo2:增加析构函数

#include <iostream>class Singleton {public:static Singleton& getInstance(){//如果对象实例不存在就创建一个if (!instance) {instance = new Singleton();}return *instance;}//给外部调用的接口void Operation(){std::cout<< "Singleton is performing some operation."<< std::endl;}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private://私有化的构造函数Singleton(){std::cout << "Singleton instance created."<< std::endl;}//私有化的析构函数~Singleton(){std::cout << "Singleton instance destroyed."<< std::endl;}static Singleton* instance;
};Singleton* Singleton::instance = nullptr;int main()
{Singleton& singleton = Singleton::getInstance();singleton.Operation();return 0;
}

运行结果:

Singleton instance created.
Singleton is performing some operation.

四、单例模式的优缺点

单例模式的优点:

可以有效限制资源的数量,对于那些需要全局访问的资源,单例模式可以保证该资源只有一个实例。

对象的创建/销毁过程最多只有一次,可以节省系统开销。

可以统一管理全局配置,如果单例模式的对象用来存储一些配置信息,可以实现对全局配置的管理。

简化了访问资源的入口,由于全局只有一个实例,使得客户端只需要关注一个访问入口即可。

 

单例模式的缺点:

缺乏灵活性,一旦创建了单例对象,就不能更改其实例化过程。

限制了一个类只能有一个实例,难以扩展该类的功能。

由于单例对象是全局可访问的,可能引发全局变量的滥用。

存在线程安全隐患,如果不增加一些锁机制,在多线程环境下可能会创建多个实例,影响单例的特性。

五、代码实战

#include <iostream>using namespace std;class Singleton
{private:static bool instanceFlag;static Singleton* single;Singleton(){printf("Private constructor finished.\n");}public:static Singleton* getInstance();void method();~Singleton(){   printf("Public de-constructor finished.\n");instanceFlag = false;}
};bool Singleton::instanceFlag = false;
Singleton* Singleton::single = NULL;Singleton* Singleton::getInstance()
{if (!instanceFlag){single = new Singleton();instanceFlag = true;return single;}else{return single;}
}void Singleton::method()
{cout << "Method of the singleton class" << endl;
}int main()
{Singleton* sc1, * sc2;sc1 = Singleton::getInstance();sc1->method();sc2 = Singleton::getInstance();sc2->method();delete sc1, sc2;return 0;
}

运行结果:

Private constructor finished.
Method of the singleton class
Method of the singleton class
Public de-constructor finished.

六、参考阅读

https://www.geeksforgeeks.org/singleton-pattern-c-design-patterns/

https://scottlilly.com/c-design-patterns-the-singleton-pattern/

https://www.tutorialspoint.com/explain-cplusplus-singleton-design-pattern

https://www.codeproject.com/Articles/1921/Singleton-Pattern-its-implementation-with-Cplusplu

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/479810.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python学习35天

# 定义父类 class Computer: CPUNone MemoryNone diskNone def __init__(self,CPU,Memory,disk): self.disk disk self.Memory Memory self.CPU CPU def get_details(self): return f"CPU:{self.CPU}\tdisk:{self.disk}\t…

<项目代码>YOLOv8 停车场空位识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

mac下Gpt Chrome升级成GptBrowser书签和保存的密码恢复

cd /Users/自己的用户名/Library/Application\ Support/ 目录下有 GPT\ Chrome/ Google/ GptBrowser/ GPT\ Chrome 为原来的chrome浏览器的文件存储目录. GptBrowser 为升级后chrome浏览器存储目录 书签所在的文件 Bookmarks 登录账号Login 相关的文件 拷贝到GptBrow…

GB28181系列二:SIP信令

我的音视频/流媒体开源项目(github) GB28181系列目录 目录 一、SIP报文介绍 二、SIP交互流程&#xff1a; 1、Session Model 2、Pager Model 3、SIP信令交互过程中的3个定义 三、媒体传输&#xff08;SDP和RTP&#xff09; 一、SIP报文介绍 这里将会介绍SIP…

ViSTa:一个包含4000多个视频和逐步描述的层次化数据集,用于评估VLMs在不同复杂性任务中的表现。

2024-11-22&#xff0c;由Google DeepMind和MATS机构创建的ViSTa数据集&#xff0c;为评估视觉语言模型&#xff08;VLMs&#xff09;在理解基于顺序的任务方面的能力提供了新的视角&#xff0c;这对于强化学习中的成本降低和安全性提升具有重要意义。 数据集地址&#xff1a;…

区块链:波场-TRON链

注意: 1、调试时请将所有的API地址都换成 https://api.trongrid.io 以免报错等问题 https://api.trongrid.io 主网 (Mainnet) 适用于生产环境 https://api.shasta.trongrid.io 测试网 (Shasta) 适用于开发者测试 https://nile.trongrid.io 测试网 (Nile) …

【适配】屏幕拖拽-滑动手感在不同分辨率下的机型适配

接到一个需求是类似下图的3D多房间视角&#xff0c;需要拖拽屏幕 问题 在做这种屏幕拖拽的时候发现&#xff0c;需要拖拽起来有跟手的感觉&#xff0c;会存在不同分辨率机型的适配问题。 即&#xff1a;美术调整好了机型1的手感&#xff0c;能做到手指按下顶层地板上下挪动&…

比特币libsecp256k1中safegcd算法形式化验证完成

1. 引言 比特币和其他链&#xff08;如 Liquid&#xff09;的安全性取决于 ECDSA 和 Schnorr 签名等数字签名算法的使用。Bitcoin Core 和 Liquid 都使用名为 libsecp256k1 的 C 库来提供这些数字签名算法&#xff0c;该库以其所运行的椭圆曲线命名。这些算法利用一种称为modu…

『VUE』elementUI dialog的子组件created生命周期不刷新(详细图文注释)

目录 1. 测试代码分析令人迷惑的效果 分析原因解决方法 如何在dialog中反复触发created呢?总结 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 主要是在做表单的时候想要有一个编辑表单在dialog弹窗中出现,同时dialog调用的封装的…

深入探讨 Redis 持久化机制:原理、配置与优化策略

文章目录 一、引言二、Redis持久化概述三、RDB&#xff08;Redis DataBase&#xff09;持久化1、RDB概念与工作原理2、RDB的配置选项3、RDB优化配置项4、RDB的优势与劣势 三、AOF&#xff08;Append-Only File&#xff09;持久化1、AOF概念与工作原理2、AOF的三种写回策略3、Re…

使用爬虫时,如何确保数据的准确性?

在数字化时代&#xff0c;数据的准确性对于决策和分析至关重要。本文将探讨如何在使用Python爬虫时确保数据的准确性&#xff0c;并提供代码示例。 1. 数据清洗 数据清洗是确保数据准确性的首要步骤。在爬取数据后&#xff0c;需要对数据进行清洗&#xff0c;去除重复、无效和…

(计算机网络)期末

计算机网络概述 物理层 信源就是发送方 信宿就是接收方 串行通信--一次只发一个单位的数据&#xff08;串行输入&#xff09; 并行通信--一次可以传输多个单位的数据 光纤--利用光的反射进行传输 传输之前&#xff0c;要对信源进行一个编码&#xff0c;收到信息之后要进行一个…

111. UE5 GAS RPG 实现角色技能和场景状态保存到存档

实现角色的技能存档保存和加载 首先&#xff0c;我们在LoadScreenSaveGame.h文件里&#xff0c;增加一个结构体&#xff0c;用于存储技能相关的所有信息 //存储技能的相关信息结构体 USTRUCT(BlueprintType) struct FSavedAbility {GENERATED_BODY()//需要存储的技能UPROPERT…

Js-对象-04-Array

重点关注&#xff1a;Array String JSON BOM DOM Array Array对象时用来定义数组的。常用语法格式有如下2种&#xff1a; 方式1&#xff1a; var 变量名 new Array(元素列表); 例如&#xff1a; var arr new Array(1,2,3,4); //1,2,3,4 是存储在数组中的数据&#xff0…

【Flink-scala】DataStream编程模型之 窗口的划分-时间概念-窗口计算程序

DataStream编程模型之 窗口的划分-时间概念-窗口计算程序 1. 窗口的划分 1.1 窗口分为&#xff1a;基于时间的窗口 和 基于数量的窗口 基于时间的窗口&#xff1a;基于起始时间戳 和终止时间戳来决定窗口的大小 基于数量的窗口&#xff1a;根据固定的数量定义窗口 的大小 这…

Java代码操作Zookeeper(使用 Apache Curator 库)

1. Zookeeper原生客户端库存在的缺点 复杂性高&#xff1a;原生客户端库提供了底层的 API&#xff0c;需要开发者手动处理很多细节&#xff0c;如连接管理、会话管理、异常处理等。这增加了开发的复杂性&#xff0c;容易出错。连接管理繁琐&#xff1a;使用原生客户端库时&…

linux系统下如何将xz及ISO\img等格式压缩包(系统)烧写到优盘(TF卡)

最近用树莓派做了个NAS&#xff0c;效果一般&#xff0c;缺少监控及UI等&#xff0c;详细见这篇文章&#xff1a; https://blog.csdn.net/bugsycrack/article/details/135344782?spm1001.2014.3001.5501 所以下载了专门的基于树莓派的NAS系统直接使用。这篇文章是顺便复习一…

带有悬浮窗功能的Android应用

android api29 gradle 8.9 要求 布局文件 (floating_window_layout.xml): 增加、删除、关闭按钮默认隐藏。使用“开始”按钮来控制这些按钮的显示和隐藏。 服务类 (FloatingWindowService.kt): 实现“开始”按钮的功能&#xff0c;点击时切换增加、删除、关闭按钮的可见性。处…

MD5算法加密笔记

MD5是常见的摘要算法。 摘要算法&#xff1a; 是指把任意⻓度的输⼊消息数据转化为固定⻓度的输出数据的⼀种密码算法. 摘要算法是 不可逆的, 也就是⽆法解密. 通常⽤来检验数据的完整性的重要技术, 即对数据进⾏哈希计算然后⽐ 较摘要值, 判断是否⼀致. 常⻅的摘要算法有: MD5…

C#变量和函数如何和unity组件绑定

1.Button On_click (1)GameObject通过Add component添加上Script (2)Button选GameObject组件而不是直接选Script,直接选Script出现不了Script中的函数 2.RawImage 上面是错的 3.Text 上面是错的&#xff0c;应该是直接在GameObject里面填上对应的值 总结&#xff1a; …