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)懒汉式(在使用时才实例化)
1 class Singleton 2 { 3 public: 4 static Singleton &GetInstance()//最好返回引用,防止用户不小心删除指针 5 { 6 if(instance == NULL) 7 instance = new Singleton(); 8 return *instance; 9 } 10 private: 11 Singleton(){} 12 ~Singleton(); 13 Singleton(const Singleton&); 14 Singleton &operator = (const Singleton&);//赋值和拷贝构造要声明为私有 15 static Singleton *instance; 16 }; 17 Singleton* Singleton::instance=NULL;//静态的数据成员必须要在类外初始化
这种实现方法没有考虑多线程安全的情况,在多线程的程序中,如果有多个线程同时访问Singleton类,调用GetInstance()方法,就有可能创建出多个实例。
(2)基于懒加载的线程安全(一)使用互斥锁
1 class Singleton 2 { 3 public: 4 static Singleton &GetInstance() 5 { 6 Lock(); 7 if(instance == NULL) 8 { 9 instance = new Singleton(); 10 } 11 Unlock(); 12 return *instance; 13 } 14 private: 15 Singleton(){} 16 ~Singleton(); 17 Singleton(const Singleton&); 18 Singleton &operator = (const Singleton&); 19 static Singleton *instance; 20 }; 21 Singleton* Singleton::instance=NULL;
Lock是用来确保当一个线程位于代码的临界区时,另一个线程不能进入临界区。这样就确保了在多线程环境下,有多个线程同时访问时只有一个线程可以进入加锁的部分,保证只创建一个实例。但是加锁这个动作会不停地在用户态和内核态之间切换,极端情况如只有单线程,这个加锁的动作会严重的拖慢效率。
(3)基于懒加载的线程安全(二)
1 class Singleton 2 { 3 public: 4 static Singleton &GetInstance() 5 { 6 if(instance == NULL) 7 { 8 Lock(); 9 if(instance == NULL) 10 { 11 instance = new Singleton(); 12 } 13 Unlock(); 14 } 15 return *instance; 16 } 17 private: 18 Singleton(){} 19 ~Singleton(); 20 Singleton(const Singleton&); 21 Singleton &operator = (const Singleton&); 22 static Singleton *instance; 23 }; 24 Singleton* Singleton::instance=NULL;
为了解决上一版本每次调用GetInstance方法是都要lock的问题,这次使用了双重if判断条件,先判断实例是否存在,如果不存在,再进行加锁。
(4)饥汉式
1 class Singleton 2 { 3 public: 4 static Singleton &GetInstance() 5 { 6 return instance; 7 } 8 private: 9 Singleton(){} 10 ~Singleton(); 11 Singleton(const Singleton&); 12 Singleton &operator = (const Singleton&); 13 static Singleton instance; 14 }; 15 Singleton Singleton::instance;
在饥汉模式下,单例类在定义时就创建了对象,对类进行了初始化,之后再有线程调用GetInstance()函数时,都返回的是同一个对象的指针,所以饥汉式是线程安全的。
但是函数外的static对象在不同编译单元中的初始化顺序是未定义的,两个单例交互就可能出现问题。