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

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

设计模式概念

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

单例模式

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

饿汉模式 

        就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。         
#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。

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

 懒汉模式

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

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

        答案是不是的。

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;

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

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++编译上面的代码,命令如下:

g++ -o main main.cpp -g

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


http://www.kler.cn/a/392050.html

相关文章:

  • vue2+ element ui 集成pdfjs-dist
  • 32位、64位、x86与x64:深入解析计算机架构
  • C++单例模式实现
  • 【再谈设计模式】抽象工厂模式~对象创建的统筹者
  • 操作系统离散存储练习题
  • linux 下查看程序启动的目录
  • openresty入门教程:ngx.print ngx.say ngx.log
  • Java LeetCode练习
  • Unity3D
  • 八、Spring Boot集成Spring Security之前后分离认证最佳实现测试
  • 多个摄像机画面融合:找到同一个目标在多个画面中的伪三维坐标,找出这几个摄像头间的转换矩阵
  • 【分布式】CAP理论
  • [2024最新] java八股文实用版(附带原理)---java集合篇
  • 大语言模型工作原理笔记
  • 机器学习——简单线性回归、逻辑回归
  • ethercat电机六自由度机械臂的ros2control+moveit2方案启动流程
  • 用Tokio掌握Rust异步编程
  • 【go从零单排】panic、recover、defer
  • 51c自动驾驶~合集10
  • python 京东api怎么写
  • 深入理解Java构造方法和接口:如何调用父类构造方法,接口能否包含方法实现?
  • MySQL缓存使用率超过80%的解决方法
  • 6.10 Queue接口/Deque接口 模拟栈与队列分析
  • 为什么谷歌SEO需要周期性维护?
  • 【excel】easy excel如何导出动态列
  • 离线安装GDAL与MapServer:在银河麒麟V10上的快速指南