一、单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式是对全局变量的一种改进。全局变量空间利用率比较低,且在项目中是一个不安全隐患,特别是在多线程程序中,会有很多的不可预测性;同时,使用全局变量,也不符合面向对象的封装原则。
二、实例
1、基础版本:
#include <iostream> using namespace std; class Singleton { public: static Singleton *GetInstance() { if (m_Instance == NULL ) { m_Instance = new Singleton (); } return m_Instance; } static void DestoryInstance() { if (m_Instance != NULL ) { delete m_Instance; m_Instance = NULL ; } } int GetTest()//该函数用于测试 { return m_Test; } private: Singleton(){ m_Test = 10; } static Singleton *m_Instance;//一个静态成员变量,程序结束时,系统会自动调用它的析构函数 int m_Test; }; Singleton *Singleton ::m_Instance = NULL; int main(int argc , char *argv []) { Singleton *singletonObj = Singleton ::GetInstance(); cout<<singletonObj->GetTest()<<endl; Singleton ::DestoryInstance(); return 0; }但该版本没有考虑多线程情况。当我们运行在多线程环境的时候,静态变量的初始化来实现单件,是不可靠的――直接的说,静态变量有可能初始化多次!
2、改进版1(对版本1进行了改进,但仍然没解决多线程问题)
#include <iostream> using namespace std; class Singleton { public: static Singleton *GetInstance() { static Singleton m_Instance;//局部静态变量,非常强大的方法,完全实现了单例的特性,而且代码量更少,也不用担心单例销毁的问题。 return &m_Instance;//返回的是一个指针,避免了问题 } int GetTest() { return m_Test++; } private: Singleton(){ m_Test = 10; }; int m_Test; }; int main(int argc , char *argv []) { Singleton *singletonObj = Singleton ::GetInstance(); cout<<singletonObj->GetTest()<<endl; singletonObj = Singleton ::GetInstance(); cout<<singletonObj->GetTest()<<endl; }
GetInstance函数如果返回引用,则Singleton singleton = Singleton :: GetInstance();这么做就出现了一个类拷贝的问题,这就违背了单例的特性。产生这个问题原因在于:编译器会为类生成一个默认的构造函数,来支持类的拷贝。
3、考虑线程安全
class Lock { private: CCriticalSection m_cs; public: Lock(CCriticalSection cs) : m_cs(cs) { m_cs.Lock(); } ~Lock() { m_cs.Unlock(); } }; class Singleton { private: Singleton(); Singleton(const Singleton &); Singleton& operator = (const Singleton &); public: static Singleton *Instantialize(); static Singleton *pInstance; static CCriticalSection cs; }; Singleton* Singleton::pInstance = 0; Singleton* Singleton::Instantialize() { if(pInstance == NULL) { //double check Lock lock(cs); //用lock实现线程安全,用资源管理类,实现异常安全 //使用资源管理类,在抛出异常的时候,资源管理类对象会被析构,析构总是发生的无论是因为异常抛出还是语句块结束。 if(pInstance == NULL) { pInstance = new Singleton(); } } return pInstance; }