设计模式_创建型模式_单例模式

在这里插入图片描述

23 种设计模式详解

13.static-----单例模式
C++单例模式

1.单例模式的要求

单例模式:一个类不管创建多少次对象,永远只能得到该类型一个对象的实例

2.应用场景

  • 1.日志模块: 一个类提供的一些方法封装了一个日志模块,软件本身有好多的功能模块,这些功能模块都需要调通过日志模块把软件运行的功能信息持久化到磁盘上。这些功能只管把日志信息给日志对象,到底怎么写,日志的格式是什么,到底往那一块写,分不分文件写,都由一个日志对象来解决,不需要用户自定义日志对象。况且软件运行过程中写日志的地方非常多,不能说每到一个写日志的地方就生成一个日志对象,如果这样就会造成一个软件有大量的日志对象,占用系统资源。日志不用那么急,最终能写进去就行。系统应该更快速的将内存资源,cpu资源,IO资源抽离出来处理客户的请求。
  • 2.数据库模块。作为一个数据库客户端去访问数据库服务器,可以调用一个数据库对象的某一个方法就可以了。不需要生成那么多对象 。一个大型的互联网公司数据库的请求是很频繁的,如果每请求一次就生成一个数据库对象,创建的连接也特别多。

3.简单单例实现

1.构造函数私有化:控制一个类生成对象的个数,就需要控制这个类的构造函数。用户如果可以随意的访问构造函数,那么用户就可以随心所欲的创建对象
2. 定义一个唯一的实例化对象。用户无法自定义对象,因此就需要为对象提供一个唯一的对象
3. 定义一个接口,用户可以通过这个接口获取唯一的实例化对象。普通常用方法依赖对象的this指针,因此该接口应该定义为static
4. 删除拷贝构造和赋值运算符重载,确保全局唯一

#include<iostream>
using namespace std;

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();

	cout << p1 << " " << p2 << " " << p3 << " " << endl;
 
	return 0;
}

饿汉式单例模式

还没有获取实例对象,实例对象就已经产生了

上面的简单单例模式实现就是典型的饿汉式。全局唯一的单例对象是静态的,在main()之前就生成并存放在数据段

优点:

因此饿汉模式的单例模式一定是线程安全的

缺点:

饿汉式实例化出唯一的对象需要调用构造函数,实际的项目中构造函数会做很多很多的事,包括加载配置信息,读文件,访问数据库等等,而这些操作应该放在第一次使用全局唯一对象的时候才干的事情。万一整个业务执行过程中就没有使用到该单例对象,那么费老半天经初始化构造该单例对象就是白费时间。况且还会延长软件的启动时间

4.懒汉式单例模式

唯一的实例对象,直到第一次获取它的时候,才产生

代码实现

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;

存在问题

问题一:是不是线程安全的

不是线程安全的

问题二:是不是可重入函数

可重入函数:这个函数还没有被执行完,那么还能不能再一次被调用

单线程程序中不存在一个函数还每被执行完就又被调用一次。但是多线程环境下就可以。如果多线程条件下,一个线程调用该函数还没有执行完,另一个线程也可以调用该函数,那么该函数就称为可重入函数。如果会发生竞态,就不是可重入函数

线程安全的懒汉模式代码实现

实现一:锁+双重循环

#include<iostream>
#include<mutex>
using namespace std;

std::mutex mtx;
#if 1//懒汉式
class Singleton
{
public:
	static Singleton* getinstance()//#3 获取类的唯─实例对象的接口方法
	{
		// lock_guard<std::mutex>guard(mtx);//锁的力度太大了,单线程环境下也需要频繁加锁解锁
		if (instance == nullptr)
		{
			lock_guard<std::mutex>guard(mtx);
			if (instance == nullptr)
			{
			/*
			开辟内存
			构造对象
			给instance赋值

			*/
				instance = new Singleton();
			}
			
		}
		return instance;
	}
private:
	static Singleton *volatile instance;//#2 定义一个唯一的类的实例对象
	//instance是一个指针存放在数据段,cpu在执行线程指令的时候,为了加快指令的执行,
	//会让线程将共享的内存数据都拷贝一份放到线程缓存里面(cpu缓存里面)
	//所以volatile给指针加,好处就是当第一个线程改变instance的值其他线程立马就能看到
	//因为线程已经不对这个共享变量进行缓存了,都是原始内存里面的值

	Singleton()//#1 构造函数私有化
	{

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

#endif


int main()
{
	Singleton* p1 = Singleton::getinstance(); 
	Singleton* p2 = Singleton::getinstance();
	Singleton* p3 = Singleton::getinstance();

	cout << p1 << " " << p2 << " " << p3 << " " << endl;
 
	return 0;
}

实现二:函数静态局部变量

函数静态局部变量的初始化,在汇编指令上已经自动添加线程互斥指令了

class Singleton
{
public:
	static Singleton* getinstance()//#3 获取类的唯─实例对象的接口方法
	{
		static Singleton instance;//#2 定义一个唯一的类的实例对象
		//静态的局部变量,存放在数据段,静态的变量第一次初始化就在第一次运行它的时候
		//如果没有调用getinstance()方法,那么instance对象是不会被构造的,不会调用构造函数,构造函数里面可能书写了好多初始化动作
		//所以这个实例是在第一次调用getinstance()方法才会产生被初始化
		return &instance;
	}
		

private:

	Singleton()//#1 构造函数私有化
	{

	}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值