单例模式的定义是:保证一个类、只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。
单例模式,顾名思义,就是只能由一个实例,那么我们就必须保证
- 该类不能被复制。
- 该类不能被公开的创造。
那么对于C++来说,他的构造函数,拷贝构造函数和他的赋值函数都不能被公开调用。
但对于该私有的构造函数的构造时机上来说也可以分两种情况来构造:
- 只有当需要改类的时候去构造(即为懒汉模式)
- 在程序开始之前我就先构造好,你到时候直接用就可(即为饿汉模式)
-
特点与选择:
由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
在访问量较小时,采用懒汉实现。这是以时间换空间。
那么我分别来说说这两种模式
a) 构造函数私有化
b) 提供一个全局的静态方法(全局访问点)
c) 在类中定义一个静态指针,指向本类的变量的静态变量指针
- 懒汉模式,静态局部变量只会被初始化一次即第一次执行的时候,其生命周期与程序的生命周期是相同的。
1 #include <iostream> 2 using namespace std; 3 4 5 //懒汉式当需要改类的时候去构造 6 class Singelton 7 { 8 private: 9 Singelton() 10 { 11 cout << "Singelton 构造函数执行" << endl; 12 } 13 public: 14 static Singelton *getInstance() 15 { 16 if (m_psl == NULL) 17 { 18 m_psl = new Singelton; 19 } 20 return m_psl; 21 } 22 23 static void FreeInstance() 24 { 25 if (m_psl != NULL) 26 { 27 delete m_psl; 28 m_psl = NULL; 29 } 30 } 31 32 private: 33 static Singelton *m_psl; 34 }; 35 36 Singelton *Singelton::m_psl = NULL; 37 38 39 void main041() 40 { 41 42 Singelton *p1 = Singelton::getInstance(); 43 Singelton *p2 = Singelton::getInstance(); 44 45 if (p1 == p2) 46 { 47 cout << "是同一个对象" << endl; 48 } 49 else 50 { 51 cout << "不是同一个对象" << endl; 52 } 53 Singelton::FreeInstance(); 54 55 56 return ; 57 } 58 59 void main() 60 { 61 main041(); 62 63 system("pause"); 64 }
饿汉模式,但是在main函数外面必须先调用其实例的构造,这个是静态实例的初始化,C/C++会保证其在进入main函数之前进行。
1 #include <iostream> 2 using namespace std; 3 4 5 //懒汉式 6 class Singelton 7 { 8 private: 9 Singelton() 10 { 11 cout << "Singelton 构造函数执行" << endl; 12 } 13 public: 14 static Singelton *getInstance() 15 { 16 return m_psl; 17 } 18 19 static void FreeInstance() 20 { 21 if (m_psl != NULL) 22 { 23 delete m_psl; 24 m_psl = NULL; 25 } 26 } 27 28 private: 29 static Singelton *m_psl; 30 }; 31 32 //int g_count = 0; 33 //饿汉式 34 Singelton *Singelton::m_psl = new Singelton; 35 36 37 void main041() 38 { 39 printf("sss "); 40 Singelton *p1 = Singelton::getInstance(); 41 Singelton *p2 = Singelton::getInstance(); 42 43 if (p1 == p2) 44 { 45 cout << "是同一个对象" << endl; 46 } 47 else 48 { 49 cout << "不是同一个对象" << endl; 50 } 51 Singelton::FreeInstance(); 52 53 return ; 54 } 55 56 void main() 57 { 58 main041(); 59 60 system("pause"); 61 }
多线程下的懒汉式单例和饿汉式单例
//1"懒汉"模式虽然有优点,但是每次调用GetInstance()静态方法时,必须判断 NULL == m_instance,使程序相对开销增大。
//2多线程中会导致多个实例的产生,从而导致运行代码不正确以及内存的泄露。
//第一个线程进来判断 sInstance == null,还没有new 出实例的时候 。这个时候第二个线程也进来了,判断的sInstance 也是 null,然后也会 new 出实例的,这样就不是我们所要的单例模式了。
//3提供释放资源的函数
讨论: 这是因为C++中构造函数并不是线程安全的。
C++中的构造函数简单来说分两步:
第一步:内存分配
第二步:初始化成员变量
由于多线程的关系,可能当我们在分配内存好了以后,还没来得急初始化成员变量,就进行线程切换,另外一个线程拿到所有权后,由于内存已经分配了,但是变量初始化还 没进行,
因此打印成员变量的相关值会发生不一致现象。
一、懒汉模式:即第一次调用该类实例的时候才产生一个新的该类实例,并在以后仅返回此实例。
需要用锁,来保证其线程安全性:原因:多个线程可能进入判断是否已经存在实例的if语句,从而non thread safety.
使用double-check来保证thread safety.但是如果处理大量数据时,该锁才成为严重的性能瓶颈。
1、静态成员实例的懒汉模式:
1 class Singleton 2 { 3 private: 4 static Singleton* m_instance; 5 Singleton(){} 6 public: 7 static Singleton* getInstance(); 8 }; 9 10 Singleton* Singleton::getInstance() 11 { 12 if(NULL == m_instance) 13 { 14 Lock();//借用其它类来实现,如boost 15 if(NULL == m_instance) 16 { 17 m_instance = new Singleton; 18 } 19 UnLock(); 20 } 21 return m_instance; 22 }
2、内部静态实例的懒汉模式
这里需要注意的是,C++0X以后,要求编译器保证内部静态变量的线程安全性,可以不加锁。但C++ 0X以前,仍需要加锁。
1 class SingletonInside 2 { 3 private: 4 SingletonInside(){} 5 public: 6 static SingletonInside* getInstance() 7 { 8 Lock(); // not needed after C++0x 9 static SingletonInside instance; 10 UnLock(); // not needed after C++0x 11 return instance; 12 } 13 };
二、饿汉模式:即无论是否调用该类的实例,在程序开始时就会产生一个该类的实例,并在以后仅返回此实例。
由静态初始化实例保证其线程安全性,WHY?因为静态实例初始化在程序开始时进入主函数之前就由主线程以单线程方式完成了初始化,不必担心多线程问题。
故在性能需求较高时,应使用这种模式,避免频繁的锁争夺