一、概念
单例模式(Singleton): 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
二、实现方法
单例模式有三种实现方法:懒汉式、双重锁定(Double-Check-Locking)、饿汉式。
1. 懒汉式
先看代码:
class Singleton //实现单例模式的类
{
private:
Singleton() {} //私有的构造函数
static Singleton* instance; //对象
public:
static Singleton* GetInstance() // 实例化创建。获得本类实例的唯一全局访问点
{
if (instance == NULL) //若实例不存在,则new一个实例;否则返回已有实例
instance = new Singleton();
return instance;
}
};
(1)构造函数声明为private,是为了防止被外部函数实例化。
(2)内部保存一个private static的类指针保存唯一的实例,实例的动作有一个public的类方法GetInstance()实现。
(3)缺点:如果是多线程同时调用GetInstance(),则可能会创建多个实例。所以再看看双重锁定,
2. 双重锁定(改进的懒汉式)
代码如下:
class Singleton //实现单例模式的类
{
private:
Singleton() {} //私有的构造函数
static Singleton* instance; //对象
public:
static Singleton* GetInstance() // 实例化创建。获得本类实例的唯一全局访问点
{
if (instance == NULL) //先判断实例是否存在,若不存在就先加把锁
{
Lock(); //加锁
if (instance == NULL) //若实例不存在,则new一个实例;否则返回已有实例
{
instance = new Singleton();
}
Unlock(); //解锁
}
return instance;
}
};
为什么加了锁之后还要判断一次 instance 是否为空呢?
我们看看这种情况:当instance为空,并且两个线程同时调用GetInstance()时,它们都会通过第一重判断instance == NULL,然后由于有锁,只能有一个线程进去,另一个线程得在外面等待,当进去的线程出来后,此时已经有实例对象了,如果没有第二重判断,那么在外等待的线程也会进去再次创建一个实例,这就不是单例了。
3.饿汉式(静态初始化)
class Singleton //实现单例模式的类
{
private:
Singleton() {} //私有的构造函数
public:
static Singleton& GetInstance() // 实例化创建。获得本类实例的唯一全局访问点
{
static Singleton instance; // 实例
return instance;
}
};
分析:
(1)饿汉式代码最简单,即使是多线程也是安全的。
(2)类一加载时就实例化对象,所以称之为饿汉式。而懒汉式是要在第一次被调用时才将自己实例化。
(3)所以,饿汉式要提前占用系统资源。而懒汉式有多线程的问题,需要用双重锁定来处理。具体使用哪一种模式要看具体的项目需求。