1. 单例模式使用的场景
在计算机中,有很多的设备比如说打印机,在打印一份文件时,只需要一个打印机工作,只要有一个打印机就可以完成这个文件的打印工作。相反的,如果系统中有多台打印机同时工作,会造成资源的浪费,也可能打出的文件是不完整的,此时就容易出现问题。
相同的,在一个类中,我们只需要有一个实例来完成它的工作就行了,此时就需要单例模式。
2. 单例模式实现的要求
要实现单例模式完成三个要点(1)只能生成一个实例,(2)这个实例必须由本类实现;(3)这个实例必须自己让系统看见自己;要实现此目的,我们需要做到三个要求:
(1)构造函数是私有的,将构造函数给成private,就不允许创建其他的对象(实例);
(2)类定义中含有一个静态的私有的对象;
(3)提供一个静态的公有的函数来获取这个对象;
3. C++实现单例模式
(1)懒汉式
#include <iostream>
using namespace std;
class CSingleton
{
public:
static CSingleton* GetInstance()
{
if (pInstance == NULL) //判断是否第一次调用
{
pInstance = new CSingleton;
}
return pInstance;
}
void ReleaseInstance()
{
delete this;
}
private:
CSingleton()
{}
CSingleton(const CSingleton& instance);
~CSingleton()
{
pInstance = NULL;
}
private:
static CSingleton *pInstance;
};
上面这种方式存在很大的问题,它只能用于单线程,如果是多线程,这个程序就没有任何意义,因为两个线程可能同时执行创建对象函数,这时就相当于创建了两个实例。
(2)我们可以利用加锁实现多线程的单例模式
C++11的锁定义在头文件<mutex>中,包含了四种不同的互斥量。
- Mutex: 提供了核心函数 lock() 和 unlock(),以及非阻塞方法的try_lock()方法,一旦互斥量不可用,该方法会立即返回。
- Recursive_mutex:允许在同一个线程中对一个互斥量的多次请求。
- Timed_mutex:同上面的mutex类似,但它还有另外两个方法 try_lock_for() 和 try_lock_until(),分别用于在某个时间段里或者某个时刻到达之间获取该互斥量。
- Recursive_timed_mutex: 结合了timed_mutex 和recuseive_mutex的使用。
我们利用lock()函数和 unlock()函数实现代码:
#include <iostream>
using namespace std;
#include <mutex>
mutex g_lock;
class CSingleton
{
public:
static CSingleton* GetInstance()
{
if (pInstance == NULL)
{
g_lock.lock();
if (pInstance == NULL) //判断是否第一次调用
{
pInstance = new CSingleton;
}
g_lock.unlock();
}
return pInstance;
}
void ReleaseInstance()
{
delete this;
}
private:
CSingleton()
{}
CSingleton(const CSingleton& instance);
~CSingleton()
{
pInstance = NULL;
}
private:
static CSingleton *pInstance;
};
在这种方式下,只有pInstance为空时即没有创建时,需要加锁操作,当pInstance创建出来时,就不需要进行加锁操作,因为加锁操作的效率很慢并且耗费资源。这样做不仅可以实现多线程的操作,也提高了效率。