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

Chapter3—单例模式

单例模式

1 概念

单例模式是指软件设计中当前系统中只有一个实例对象,同时提供集中、统一的访问接口,以使系统行为保持协调一致。

2 意图(Intent)

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

3 动机(Motivate)

在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。如何绕过常规的构造器,提供一种机制来保证一个类只创建一个实例?这应该是类设计者的责任,而不是类使用者的责任。

4 类图结构

在这里插入图片描述

5 角色定义

  • Singleton(单例类):包含一个自己的类实例的属性,并把构造方法用private关键字隐藏起来,只对外提供GetInstance()方法以获得这个单例对象。

6 程序示例

饿汉式:在调用之初就创建

/*** @brief 饿汉式单例类实现*/ 
class Singleton
{
private:Singleton() = default;public:~Singleton() = default;Singleton(const Singleton& src) = delete;Singleton(Singleton&& src) = delete;Singleton& operator=(const Singleton& src) = delete;Singleton& operator=(Singleton&& src) = delete;public:void Show();public:static Singleton* GetInstance();static void DestroyInstance();private:static Singleton* m_spInstance;
};void Singleton::Show()
{qDebug() << "我是一个单例类";
}Singleton *Singleton::GetInstance()
{return m_spInstance;
}void Singleton::DestroyInstance()
{if (nullptr != m_spInstance){delete m_spInstance;m_spInstance = nullptr;}
}Singleton* Singleton::m_spInstance = new Singleton();			// 静态创建,系统初始化的时候自动就创建了/*** @brief 饿汉式单例测试函数*/ 
void TestSingleton()
{Singleton::GetInstance()->Show();Singleton::DestroyInstance();
}

懒汉式:在调用之后再创建

/*** @brief 懒汉式单例类实现*/
class Singleton
{
private:Singleton() = default;public:~Singleton() = default;Singleton(const Singleton& src) = delete;Singleton(Singleton&& src) = delete;Singleton& operator=(const Singleton& src) = delete;Singleton& operator=(Singleton&& src) = delete;public:void Show();public:static Singleton* GetInstance();static void DestroyInstance();private:static Singleton* m_spInstance;
};void Singleton::Show()
{qDebug() << "我是一个单例类";
}Singleton *Singleton::GetInstance()
{if (nullptr == m_spInstance)m_spInstance = new Singleton();         // 在第一次调用的时候才创建return m_spInstance;
}void Singleton::DestroyInstance()
{if (nullptr != m_spInstance){delete m_spInstance;m_spInstance = nullptr;}
}Singleton* Singleton::m_spInstance = nullptr;   // 初始化为 nullptr/*** @brief 懒汉式单例测试函数*/ 
void TestSingleton()
{Singleton::GetInstance()->Show();Singleton::DestroyInstance();
}

局部静态变量方式

此方式代码简洁且高效,这种好处就是不用管理内存,局部静态变量会自动析构,对于饿汉式和懒汉式都需要去手动释放下内存。

/*** @brief 局部静态变量方式的单例类实现*/
class Singleton
{
private:Singleton() = default;public:~Singleton() = default;Singleton(const Singleton& src) = delete;Singleton(Singleton&& src) = delete;Singleton& operator=(const Singleton& src) = delete;Singleton& operator=(Singleton&& src) = delete;public:void Show();public:static Singleton* GetInstance();
};void Singleton::Show()
{qDebug() << "我是一个单例类";
}Singleton *Singleton::GetInstance()
{static Singleton instance;return &instance;
}/*** @brief 单例测试函数,不需要主动调用释放内存了*/ 
void TestSingleton()
{Singleton::GetInstance()->Show();
}

懒汉式单例线程安全

#include <mutex>using std::string;
using std::vector;
using std::mutex;/*** @brief 懒汉式单例类实现*/
class Singleton
{
private:Singleton() = default;public:~Singleton() = default;Singleton(const Singleton& src) = delete;Singleton(Singleton&& src) = delete;Singleton& operator=(const Singleton& src) = delete;Singleton& operator=(Singleton&& src) = delete;public:void Show();public:static Singleton* GetInstance();static void DestroyInstance();private:static Singleton* m_spInstance;static std::mutex m_sMutex;             // 增加锁
};void Singleton::Show()
{qDebug() << "我是一个单例类";
}Singleton *Singleton::GetInstance()
{// 双重检查锁定是一种高效的线程安全实现方式,通过减少锁的使用频率来提升性能。// 如果直接加在最外面,每调用一次就得进行一次加锁,这样效率十分低下if (nullptr == m_spInstance){std::lock_guard<std::mutex> lockGuard(m_sMutex);if (nullptr == m_spInstance)m_spInstance = new Singleton();}return m_spInstance;
}void Singleton::DestroyInstance()
{// 析构函数不会经常调用,所以直接在最外层加锁即可std::lock_guard<std::mutex> lockGuard(m_sMutex);if (nullptr != m_spInstance){delete m_spInstance;m_spInstance = nullptr;}
}Singleton* Singleton::m_spInstance = nullptr;   // 初始化为 nullptrstd::mutex Singleton::m_sMutex = std::mutex();/*** @brief 单例测试函数*/
void TestSingleton()
{Singleton::GetInstance()->Show();Singleton::DestroyInstance();
}

模板类单例

单例模板类:

#include <mutex>using std::string;
using std::vector;
using std::mutex;template <typename T>
class Singleton
{
public:template <typename... Args>static void InitInstance(Args... args);static T* GetInstance();static void DestroyInstance();private:static T* sm_pInstance;static mutex sm_Mutex;private:Singleton() = default;~Singleton() = default;Singleton(const Singleton& src) = delete;Singleton(Singleton&& src) = delete;Singleton& operator=(const Singleton& src) = delete;Singleton& operator=(Singleton&& src) = delete;
};template <typename T>
T* Singleton<T>::sm_pInstance = nullptr;template <typename T>
mutex Singleton<T>::sm_Mutex = mutex();template<typename T>
template<typename... Args>
void Singleton<T>::InitInstance(Args... args)
{if (nullptr == sm_pInstance){std::lock_guard<mutex> lockGuard(sm_Mutex);if (nullptr == sm_pInstance)sm_pInstance = new T(std::forward<Args>(args)...);      // 参数完美转发}
}template<typename T>
T *Singleton<T>::GetInstance()
{return sm_pInstance;
}template<typename T>
void Singleton<T>::DestroyInstance()
{std::lock_guard<mutex> lockGuard(sm_Mutex);if (nullptr != sm_pInstance){delete sm_pInstance;sm_pInstance = nullptr;}
}

一般类:

class Person
{
public:Person(const string& strName, int nAge);~Person();public:void Show();private:string m_strName;int m_nAge;
};Person::Person(const string &strName, int nAge):m_strName(strName), m_nAge(nAge)
{qDebug() << "Create Perosn";
}Person::~Person()
{qDebug() << "Release Perosn";
}void Person::Show()
{qDebug() << "[Name =" << m_strName.c_str() << ", Age =" << m_nAge << "]";
}

采用模板单例类框架生成一个一般类的单例类:

void TestSingleton()
{Singleton<Person>::InitInstance("ZhangSan", 23);Singleton<Person>::GetInstance()->Show();Singleton<Person>::DestroyInstance();
}

7 思考小结

当系统中某个类型仅需要一个对象时候可以考虑使用单例模式。在C++开发中,我们也通常建议使用局部静态变量的方式,也就是第三种方式。单例模式一直是一个有争议的设计模式,因为其本身不存在抽象的概念,没有虚函数(接口),系统难以进行扩展,所以有些人甚至建议将单例模式剔除出设计模式的范畴。

8 附录

如何实现饿汉式单例的内存自动释放?思路有点类似于C++的RAII,我们拿一个身份证类的单例来实现,程序代码如下

class IdentityCard
{
private:struct AutoRelease              // 内部类,负责单例对象自动析构,在析构函数中释放单例对象{AutoRelease(){}~AutoRelease(){if (nullptr != sm_pInstance){delete sm_pInstance;sm_pInstance = nullptr;}}};private:IdentityCard();public:~IdentityCard();public:IdentityCard(const IdentityCard& src) = delete;IdentityCard(IdentityCard&& src) = delete;IdentityCard& operator=(const IdentityCard& src) = delete;IdentityCard& operator=(IdentityCard&& src) = delete;public:static IdentityCard* GetInstance();private:static IdentityCard* sm_pInstance;static AutoRelease sm_AutoRelease;              // 声明为静态成员变量,在程序结束时会自动调用析构函数,从而释放掉单例的对象public:void SetNumber(const string& strNumber);string GetNumber();string m_strNumber;
};IdentityCard* IdentityCard::sm_pInstance = new IdentityCard();IdentityCard::AutoRelease IdentityCard::sm_AutoRelease = IdentityCard::AutoRelease();IdentityCard::IdentityCard()
{qDebug() << "Create IdentityCard!";
}IdentityCard::~IdentityCard()
{qDebug() << "Release IdentityCard!";
}IdentityCard *IdentityCard::GetInstance()
{return sm_pInstance;
}void IdentityCard::SetNumber(const string &strNumber)
{m_strNumber = strNumber;
}string IdentityCard::GetNumber()
{return m_strNumber;
}

自动释放内存的单例测试函数

void TestIdentityCard()
{IdentityCard::GetInstance()->SetNumber("ACB");qDebug() << IdentityCard::GetInstance()->GetNumber().c_str();
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);TestIdentityCard();return 0;
}

输出结果

Create IdentityCard!
ACB
Release IdentityCard!

内存的释放过程如下图
在这里插入图片描述


文章转载自:

http://T5woIvHg.zyffq.cn
http://YLK3uxrY.zyffq.cn
http://YC18gnkC.zyffq.cn
http://hF88mp4f.zyffq.cn
http://koh1bleu.zyffq.cn
http://9XSoS41t.zyffq.cn
http://a4nQ3iI5.zyffq.cn
http://4tuZAIcq.zyffq.cn
http://2WHN0Mza.zyffq.cn
http://88NbGzGR.zyffq.cn
http://onmzPK4s.zyffq.cn
http://RAH4IIoC.zyffq.cn
http://UzZ3tsex.zyffq.cn
http://xCyx5IFg.zyffq.cn
http://q8TP8Prj.zyffq.cn
http://nrFq6yQL.zyffq.cn
http://gXFqJ6Eh.zyffq.cn
http://Mxvs6RN1.zyffq.cn
http://6yGNvqmq.zyffq.cn
http://7w7UTpFO.zyffq.cn
http://y2pdVrVf.zyffq.cn
http://ONIijGGQ.zyffq.cn
http://Vh2o9Z6Q.zyffq.cn
http://22wN1nOr.zyffq.cn
http://zcCYnqFz.zyffq.cn
http://jbp0xBws.zyffq.cn
http://phYHo38L.zyffq.cn
http://ts4LI5wx.zyffq.cn
http://OdcaeiXp.zyffq.cn
http://cA07ibeu.zyffq.cn
http://www.dtcms.com/a/372786.html

相关文章:

  • k8s可视化的解决方案及技术选型
  • K8s Ingress Annotations参数使用指南
  • Kubernetes(K8S)入门以及命令指南
  • 自建prometheus监控腾讯云k8s集群
  • Go 1.25在性能方面做了哪些提升?
  • Next.js数据获取入门:`getStaticProps` 与 `getServerSideProps`
  • 为什么要在出口路由器router配置NAT与默认路由
  • 如何 正确使用 nrm 工具 管理镜像源
  • http response的工作流程详细解析
  • FastDFS(分布式RPC调用和分布式文件储存)
  • 国内开源时序数据库IoTDB介绍
  • TCL电视机音乐播放器动效背景模仿
  • 深入解析:Vue与React的异步批处理更新机制
  • 基于Spring Boot的火灾报警系统的设计与实现(代码+数据库+LW)
  • Spring Boot的配置文件加载顺序和规则
  • B.30.10.05-JVM电商实战应用
  • vulhub fastjson 1.2.24 反序列化导致任意命令执行漏洞
  • [特殊字符] 跨端视频通话实战:腾讯云 TRTC + IM(React Native Web)
  • 【重学 MySQL】九十八、MySQL用户管理全指南:创建、修改、删除
  • 2025时序数据库选型,以IoTDB为主从架构基因到AI赋能来解析
  • 如何用表单快速构建一个用户反馈系统?
  • 2020/12 JLPT听力原文 问题四
  • 基于ConvFormer的双条件域自适应方法的故障诊断模型
  • Day 14: RAG检索增强生成核心技术 - 从原理到实战的完整指南 [特殊字符]
  • mysql 回表查询(二次查询,如何检查,如何规避)
  • vue3+ts使用html2canvas,实现页面截图
  • 疾病语音数据集 WAV格式音频
  • 07 下载配置很完善的yum软件源
  • 【PCIe EP 设备入门学习专栏 -- 8.2.2 PCIe EP Controller Register Types 介绍】
  • 排序---冒泡排序(Bubble Sort)