【设计模式】单例设计模式

设计模式概念

设计模式(Design Pattern )是一套 被反复使用、多数人知晓的、经过分类的、代码设计经验的 总结 。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路 的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

单例模式

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个 访问它的全局访问点,该实例被所有程序模块共享 。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再
通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
单例模式有两种实现模式:

饿汉模式

就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。

cpp 复制代码
#include <iostream>
class Singleton
{
public:
	static Singleton* getInstance()	// 3.获取类的唯一实例对象的接口方法
	{
		return &instance;
	}
private:
	static Singleton instance; // 2.定义一个唯一的类的实例对象
	Singleton() // 1.构造函数私有化
	{

	}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
};
Singleton Singleton::instance;

int main()
{
	Singleton* p1 = Singleton::getInstance();
	Singleton* p2 = Singleton::getInstance();
	Singleton* p3 = Singleton::getInstance();
	std::cout << p1 << std::endl;
	std::cout << p2 << std::endl;
	std::cout << p3 << std::endl;
	return 0;
}

因为单例模式只能实例化出一个对象,所以p1 = p2 = p3。


如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。

懒汉模式

如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取
文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化, 就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。
唯一的实例对象,直到第一次获取它的时候,才产生。

cpp 复制代码
#include <iostream>
class Singleton
{
public:
	static Singleton* getInstance()	// 3.获取类的唯一实例对象的接口方法
	{
		if (instance == nullptr)
		{
			instance = new Singleton();
		}
		return instance;
	}
private:
	static Singleton *instance; // 2.定义一个唯一的类的实例对象
	Singleton() // 1.构造函数私有化
	{

	}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
};
Singleton *Singleton::instance = nullptr;

int main()
{
	Singleton* p1 = Singleton::getInstance();
	Singleton* p2 = Singleton::getInstance();
	Singleton* p3 = Singleton::getInstance();
	std::cout << p1 << std::endl;
	std::cout << p2 << std::endl;
	std::cout << p3 << std::endl;
	return 0;
}

我们把Singleton类中的实例对象改为指针,在外部实例化的时候我们初始化为nullptr, 我们在getInstance()函数中,如果instamce是nullptr的话,就给它new一块内存来实例化一个对象,如果不是空,返回已经实例化的那个对象。

懒汉式单例模式是不是线程安全的呢????

答案是不是的。

cpp 复制代码
std::mutex mtx;
class Singleton
{
public:
	static Singleton* getInstance()	// 3.获取类的唯一实例对象的接口方法
	{
		// std::lock_guard<std::mutex> lock(mtx);	// 锁的粒度太大了,单线程的时候也要不断加锁解锁,太浪费了
		if (instance == nullptr)
		{
			std::lock_guard<std::mutex> lock(mtx);
			if (instance == nullptr) {
				instance = new Singleton();
			}
		}
		return instance;
	}
private:
	static Singleton *volatile instance; // 2.定义一个唯一的类的实例对象
	Singleton() // 1.构造函数私有化
	{

	}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
};
Singleton *volatile Singleton::instance = nullptr;

上面的代码使用锁 + 双重判断来保证线程安全。

cpp 复制代码
class Singleton
{
public:
	static Singleton* getInstance()	// 3.获取类的唯一实例对象的接口方法
	{
		// std::lock_guard<std::mutex> lock(mtx);	// 锁的粒度太大了,单线程的时候也要不断加锁解锁,太浪费了
		static Singleton instance;
		return &instance;
	}
private:
	Singleton() // 1.构造函数私有化
	{}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
};

上面的这段代码,也是在第一次调用getInstance方法的时候才实例化对象,它也是线程安全的,因为函数静态局部变量的初始化,在汇编指令上已经自动添加线程互斥指令了。

在Linux环境中,通过g++编译上面的代码,命令如下:

bash 复制代码
g++ -o main main.cpp -g

生成可执行文件main,用gdb进行调试,到getInstance函数,并打印该函数的汇编指令,如下:

相关推荐
学长毕设3 小时前
基于SpringBoot的青少年编程学习平台系统-附源码
spring boot·学习·青少年编程
知守观5 小时前
设计模式实战:从真实老项目中看标准模板方法与工厂的落地
java·spring·设计模式
choumin5 小时前
C++中万能引用与std::forward的作用和用法
c++·万能引用·std forward
下一站春风里6 小时前
位运算完整学习笔记:按位与(&)+ 异或(^)
开发语言·c++·算法
小小de风呀7 小时前
de风——【从零开始学习Linux】(九):进程基础 —— 从冯诺依曼体系到 fork 创建进程
linux·学习
李某学编程7 小时前
基于ARM的微机原理学习笔记(一)
arm开发·笔记·学习
Song_RsHt8 小时前
Redis 与微服务,什么场景下无可替代
java·redis·后端·学习·微服务
sbjdhjd8 小时前
AI产业周度动态:从“更强模型”走向“可执行、可治理、可计价”的系统竞争 (2026年10月19日—10月25日)
人工智能·笔记·ai·开源·gemini·信息差·claude code
努力努力再努力wz9 小时前
【C++高阶系列】C++20 协程底层原理:函数为什么能暂停?从状态机到 Task,彻底串起挂起、恢复与协程调度
开发语言·c++·c++20·1024程序员节
划破黑暗的第一缕曙光9 小时前
[C++]:4.模板
c++·template·模板·函数模版·类模版