1.单例模式(Singleton):由于某种需要,要保证一个类在程序的生命周期中只有一个实例,并提供一个该实例的全局访问方法。
2.单例模式(Singleton)结构图:
Singleton类,定义了一个GetInstance操作,允许客户访问他的唯一实例,
GetInstance是一个静态方法,主要负责创建自己的唯一实例
3.特点:
(1)一个类只能有一个实例
(2)该类可以自行创建这个实例
(3)该类能向整个系统返回这个实例
4.单例模式一般包含三个要素:
(1)私有的静态的实例对象 private static instance
(2)私有的构造函数 (保证在该类外部无法通过new方法创建实例对象) private Singleton(){}
(3)公有的静态的访问该实例的方法 public static Singleton GetInstance(){}
5.单例模式的实现
(1)懒汉式(在使用时才实例化)
class Singleton
{
public:
static Singleton &GetInstance()//最好返回引用,防止用户不小心删除指针
{
if(instance == NULL)
instance = new Singleton();
return *instance;
}
private:
Singleton(){}
~Singleton();
Singleton(const Singleton&);
Singleton &operator = (const Singleton&);//赋值和拷贝构造要声明为私有
static Singleton *instance;
};
Singleton* Singleton::instance=NULL;//静态的数据成员必须要在类外初始化
这种实现方法没有考虑多线程安全的情况,在多线程的程序中,如果有多个线程同时访问Singleton类,调用
GetInstance()方法,就有可能创建出多个实例
(2)基于懒加载的线程安全(一)使用互斥锁
class Singleton
{
public:
static Singleton &GetInstance()
{
Lock();
if(instance == NULL)
{
instance = new Singleton();
}
Unlock();
return *instance;
}
private:
Singleton(){}
~Singleton();
Singleton(const Singleton&);
Singleton &operator = (const Singleton&);
static Singleton *instance;
};
Singleton* Singleton::instance=NULL;
Lock是用来确保当一个线程位于代码的临界区时,另一个线程不能进入临界区。这样就确保了在多线程环
境下,有多个线程同时访问时只有一个线程可以进入加锁的部分,保证只创建一个实例。
但是加锁这个动作会不停地在用户态和内核态之间切换,极端情况如只有单线程,这个加锁的动作会严重的拖慢效率。
(3)基于懒加载的线程安全(二)
class Singleton
{
public:
static Singleton &GetInstance()
{
if(instance == NULL)
{
Lock();
if(instance == NULL)
{
instance = new Singleton();
}
Unlock();
}
return *instance;
}
private:
Singleton(){}
~Singleton();
Singleton(const Singleton&);
Singleton &operator = (const Singleton&);
static Singleton *instance;
};
Singleton* Singleton::instance=NULL;
为了解决上一版本每次调用GetInstance方法是都要lock的问题,这次使用了双重if判断条件,先判断实
例是否存在,如果不存在,再进行加锁。
(4)饥汉式
class Singleton
{
public:
static Singleton &GetInstance()
{
return instance;
}
private:
Singleton(){}
~Singleton();
Singleton(const Singleton&);
Singleton &operator = (const Singleton&);
static Singleton instance;
};
Singleton Singleton::instance;
在饥汉模式下,单例类在定义时就创建了对象,对类进行了初始化,之后再有线程调用GetInstance()
函数时,都返回的是同一个对象的指针,所以饥汉式是线程安全的。
但是函数外的static对象在不同编译单元中的初始化顺序是未定义的,两个单例交互就可能出现问题。