C++设计模式-创建型设计模式:单例模式

目录

单例模式简介

单例模式的典型疑问与优缺点:饿汉、懒汉与多线程安全

单例模式的扩展与应用-缓存


单例模式简介

C++设计模式-创建型设计模式:单例模式_第1张图片

每次程序运行都要读config.ini,由生成的对象传入参数,但计算机读取IO缓慢,而且这些配置基本不会变化,如果每次运行程序都要重新new这个对象,会带来很多不必要的损耗。

那么就需要整个程序运行中有且只有一个全局的对象,这个需求点就是单例模式。

#include 
#include 
using namespace std;

//外部程序没有机会new这个class
class Singleton {
private:
	Singleton() {
		m_singer = nullptr;
		cout << "构造一个SIngleton" << endl;
	}
public:
	static Singleton* getInstance() {
		if (m_singer == nullptr) {
			m_singer = new Singleton;
		}
		return m_singer;
	}
private:
	static Singleton* m_singer;
};


Singleton* Singleton::m_singer = nullptr;//懒汉式:延迟加载
//Singleton* Singleton::m_singer = new Singleton;//饿汉式
//C++中构造函数是线程不安全的
int main() {
	Singleton* p1 = Singleton::getInstance();
	Singleton* p2 = Singleton::getInstance();
	printf("%x,%x", p1, p2);
	return 0;
}

C++设计模式-创建型设计模式:单例模式_第2张图片

 

单例模式的典型疑问与优缺点:饿汉、懒汉与多线程安全

上面的例子就是懒汉式单例。

但是如果这个单例是多线程,会出现什么问题?

C++设计模式-创建型设计模式:单例模式_第3张图片

C++设计模式-创建型设计模式:单例模式_第4张图片

假设线程1在运行到m_singer=null时时间片轮转完了,那么此时就会切换到线程2中。

此时线程2也会进入到m_singer=null 这个if语句块中,并创建一个Singleton。

此时线程2结束,切回线程1,线程1继续执行刚才的现场,又创建了一个Singleton!

那么如何解决这个问题,根据操作系统的知识,我们需要为互斥执行的语句创建临界区,并进行互斥访问。

C++设计模式-创建型设计模式:单例模式_第5张图片

饿汉式与懒汉式的区别在于,懒汉式是一种延迟加载的方式,刚开始赋值为null,直到加载的时候才创建对象;而饿汉式在一开始初始化的时候就创建了对象,分配了资源。

因为计算机进行I/O操作十分缓慢,因此延迟加载是一种提升性能的手段。有助于资源的合理使用。

 

单例模式的扩展与应用-缓存

  • 既然我们可以控制全局生成一个对象,那么有没有需要生成一个以上的对象呢?
  • 缓存设计与Singleton的扩展

集群。一台机器再强大,也不一定能抗住所有人的访问,所以可以用多台机器搭建一个集群。

//回避多线程的安全问题
#include 
#include 	
#include 
using namespace std;

class Singleton;
static std::mapmyMap = std::map();

//懒汉,延迟加载
class Singleton {
private:
	Singleton() {
		m_singer = nullptr;
		cout << "单例正在创建" << endl;
	}
public:
	static Singleton* getInstance() {
		//std::map::iterator it = myMap.find(DEFAULT_KEY);
		if (myMap.find(DEFAULT_KEY) != myMap.end()) {
			return myMap.find(DEFAULT_KEY)->second;
		}
		if (m_singer == nullptr) {
			m_singer = new Singleton;
			myMap[DEFAULT_KEY] = m_singer;
		}
		return m_singer;
	}

private:
	static Singleton* m_singer;
	static string DEFAULT_KEY;
};

Singleton* Singleton::m_singer = nullptr;
string Singleton::DEFAULT_KEY = "one";

int main() {
	Singleton* p1 = Singleton::getInstance();
	Singleton* p2 = Singleton::getInstance();
	printf("p1=%x, p2=%x\n", p1, p2);
	return 0;
}

C++设计模式-创建型设计模式:单例模式_第6张图片 

 

如果我们对这个DEFAULT_KEY做一些事情,让它成为多例。

#include 
#include 	
#include 
#include 
using namespace std;

//缓存实例个数
const static int NUM_MAX = 5;
class Singleton;
static std::mapmyMap = std::map();

//懒汉,延迟加载
class Singleton {
private:
	Singleton() {
		m_singer = nullptr;
		cout << "单例正在创建" << endl;
	}
public:
	static Singleton* getInstance() {
		m_singer = myMap[m_InstanceCount];

		if (m_singer == nullptr) {
			m_singer = new Singleton;
			myMap[m_InstanceCount] = m_singer;
		}
		m_InstanceCount++;
		if (m_InstanceCount > NUM_MAX) {
			m_InstanceCount = 1;
		}
		return m_singer;
	}

private:
	static Singleton* m_singer;
	static int m_InstanceCount;//存放实例个数
};

Singleton* Singleton::m_singer = nullptr;
int Singleton::m_InstanceCount = 1;

int main() {
	Singleton* p1 = Singleton::getInstance();
	Singleton* p2 = Singleton::getInstance();
	Singleton* p3 = Singleton::getInstance();
	Singleton* p4 = Singleton::getInstance();
	Singleton* p5 = Singleton::getInstance();
	printf("p1=%x, \np2=%x, \np3=%x, \np4=%x, \np5=%x, \n\n", p1, p2, p3, p4, p5);	
	Singleton* p6 = Singleton::getInstance();
	Singleton* p7 = Singleton::getInstance();
	Singleton* p8 = Singleton::getInstance();
	Singleton* p9 = Singleton::getInstance();
	Singleton* p10 = Singleton::getInstance();
	printf("p6=%x, \np7=%x, \np8=%x, \np9=%x, \np10=%x, ", p6, p7, p8, p9, p10);
	return 0;
}

C++设计模式-创建型设计模式:单例模式_第7张图片

代码中写了10个instance,但是实际上只有5个instance被创建。 

在实际应用中,可能集群需要维护5个单例,以便多用户访问和使用。

 

你可能感兴趣的:(设计模式,c++,设计模式,单例模式)