单例模式

很多时候,我们在程序设计的时候,对于某一类只需要生成其中一个对象。比如说对于MySQL数据库类来说,如果频繁的创建MySQL对象,那么会有海量的连接连接到MySQL数据库,对数据库来说是一个不小的负担。

但其实对于开发者来说,一个连接其实就能胜任与数据库的交互工作,这就是单例模式。

两种单例模式

而对于单例模式来说,又分为两种:懒汉式、饿汉式

两种单例模式:
懒汉式: 当获取实例对象时,对象才产生
饿汉式: 还没有获取实例对象时,对象已经产生了

饿汉式

可能大家已经想到了,利用static修饰的变量属于类这一特性来实现。确实也是这么做的:

1.构造函数私有化
2.定义static修饰的唯一实例对象
3.类外初始化
4.类中提供一个供外界获取的static接口
class  Singleton
{
   
public:
	static Singleton* get_instance()
	{
   
		return &instance;
	}
private:
	static Singleton instance;	//唯一对象
	Singleton() //构造函数私有化
	{
   
	}

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

Singleton Singleton::instance;

这里我们可以看到,无论是多线程还是单线程,对于饿汉式来说,都是线程安全的。

懒汉式

既然有了饿汉式,而且饿汉式在多线程方面还是近乎完美的,那么又要懒汉式单例模式干什么呢?

设想一种情况:一个项目中有很多需要单例设计的类,如果其中大部分类呢又用不到,但是饿汉式会让他们都生成实例对象,这样就造成了内存资源一种浪费

所以就需要懒汉式设计模式

1.构造函数私有化
2.类中设计一个static修饰的指针并在类外初始化
3.提供一个供外部访问的static修饰的函数接口
4.函数接口中实现new一个实例对象
mutex g_mutex;
class  Singleton
{
   
public:
	static Singleton* get_instance()
	{
   
		if (instance == nullptr)
		{
   
			lock_guard<mutex> lock(mutex);
			if (instance == nullptr)
			{
   
				instance = new Singleton();
			}
		}
		return instance;
	}
private:
	static Singleton *volatile instance;	//唯一对象指针
	//volatile 编译器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份
	
	Singleton() //构造函数私有化
	{
   
	}

	Singleton(const Singleton&) = delete;
	Singleton operator=(const Singleton&) = delete;
};
static Singleton* volatile instance = nullptr;	//唯一对象

可以看到,我这里是加了一把锁g_mutex的,而且在对外提供的static函数接口内实现了双重 if 判断,这是因为,如果不加锁的话:

static Singleton* get_instance()
{
   
	if (instance == nullptr)
	{
   
		instance = new Singleton();
	}
	return instance;
}

试想一种情况:如果两个线程同时进入get_instance函数,并且此时他们进入时的instance都是nullptr,那么这两个线程都会new一个Singleton对象,但是instance只能去指向其中的一个,这样就会造成有一个Singleton的内存泄漏。

而在锁后又加一层 if 判断的原因是:

if (instance == nullptr)
{
   
	lock_guard<mutex> lock(mutex);
	if (instance == nullptr)
	{
   
		instance = new Singleton();
	}
}

还是两个线程,进入都判断instance为nullptr,都通过了第一个if,其中线程A获得锁,创造了一个Singleton对象,然后释放锁;线程B 获得锁,如果不加第二个 if 的话无疑会再次创造一个实例对象,这是我们不希望看见的。

参考文献

[1] 施磊.C++高级.图论科技.2020.7.