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

设计模式概念

设计模式(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函数,并打印该函数的汇编指令,如下:

相关推荐
无所谓จุ๊บ5 分钟前
VTK知识学习(6)-使用颜色
学习·vtk
_小柏_38 分钟前
C/C++基础知识复习(18)
c语言·c++
闲人编程42 分钟前
爬虫反爬机制和解决方案
开发语言·c++·爬虫·python·验证码
iFlyCai44 分钟前
23种设计模式的Flutter实现第一篇创建型模式(一)
flutter·设计模式·dart
zhouzhihao_071 小时前
程序代码设计模式之模板方法模式(1)
java·设计模式·模板方法模式
xianwu5431 小时前
【设计模式】工厂模式
开发语言·c++·设计模式·简单工厂模式·抽象工厂模式
进阶的架构师1 小时前
阿里巴巴最新“SpringCloudAlibaba 学习笔记”开源。
笔记·学习·开源
love_and_hope2 小时前
Pytorch学习--神经网络--利用GPU训练
人工智能·pytorch·python·神经网络·学习·数据挖掘
Enoch8882 小时前
Day09 C++ 存储类
java·jvm·c++
ssma1112 小时前
ES6笔记
前端·笔记·es6