1. 程式人生 > >三種單例模式的C++實現

三種單例模式的C++實現

簡介

        因為在設計或開發中,肯定會有這麼一種情況,一個類只能有一個物件被建立,如果有多個物件的話,可能會導致狀態的混亂和不一致。這種情況下,單例模式是最恰當的解決辦法。它有很多種實現方式,各自的特性不相同,使用的情形也不相同。今天要實現的是常用的三種,分別是餓漢式、懶漢式和多執行緒式。

        通過單例模式, 可以做到:

1. 確保一個類只有一個例項被建立 
2. 提供了一個對物件的全域性訪問指標 
3. 在不影響單例類的客戶端的情況下允許將來有多個例項

懶漢式

      懶漢式的特點是延遲載入,比如配置檔案,採用懶漢式的方法,顧名思義,懶漢麼,很懶的,配置檔案的例項直到用到的時候才會載入。。。。。。

  1. class CSingleton  
  2. {  
  3. public:  
  4. static CSingleton* GetInstance()  
  5. {  
  6.      if ( m_pInstance == NULL )    
  7.          m_pInstance = new CSingleton();  
  8.      return m_pInstance;  
  9. }  
  10. private:  
  11.     CSingleton(){};  
  12.     static CSingleton * m_pInstance;  
  13. };  

GetInstance()使用懶惰初始化,也就是說它的返回值是當這個函式首次被訪問時被建立的。這是一種防彈設計——所有GetInstance()之後的呼叫都返回相同例項的指標:

CSingleton* p1 = CSingleton :: GetInstance();
CSingleton* p2 = p1->GetInstance();
CSingleton & ref = * CSingleton :: GetInstance();
對GetInstance稍加修改,這個設計模板便可以適用於可變多例項情況,如一個類允許最多五個例項。

程式碼很簡單,但是會存在記憶體洩漏的問題,new出來的東西始終沒有釋放,下面是一種餓漢式的一種改進。

  1. class CSingleton    
  2. {    
  3. private:    
  4.     CSingleton()    
  5.     {    
  6.     }    
  7.     static CSingleton *m_pInstance;    
  8.     class CGarbo     
  9.     {    
  10.     public:    
  11.         ~CGarbo()    
  12.         {    
  13.             if(CSingleton::m_pInstance)    
  14.                 delete CSingleton::m_pInstance;    
  15.         }    
  16.     };    
  17.     static CGarbo Garbo;     
  18. public:    
  19.     static CSingleton * GetInstance()    
  20.     {    
  21.         if(m_pInstance == NULL)    
  22.             m_pInstance = new CSingleton();    
  23.         return m_pInstance;    
  24.     }    
  25. };    

    在程式執行結束時,系統會呼叫CSingleton的靜態成員Garbo的解構函式,該解構函式會刪除單例的唯一例項。使用這種方法釋放單例物件有以下特徵:
1.在單例類內部定義專有的巢狀類。
2.在單例類內定義私有的專門用於釋放的靜態成員。
3.利用程式在結束時析構全域性變數的特性,選擇最終的釋放時機。

餓漢式

       餓漢式的特點是一開始就載入了,如果說懶漢式是“時間換空間”,那麼餓漢式就是“空間換時間”,因為一開始就建立了例項,所以每次用到的之後直接返回就好了。

  1. class CSingleton    
  2. {    
  3. private:    
  4.     CSingleton()      
  5.     {    
  6.     }    
  7. public:    
  8.     static CSingleton * GetInstance()    
  9.     {    
  10.         static CSingleton instance;     
  11.         return &instance;    
  12.     }    
  13. };    

      餓漢式是執行緒安全的,在類建立的同時就已經建立好一個靜態的物件供系統使用,以後不再改變,懶漢式如果在建立例項物件時不加上synchronized則會導致對物件的訪問不是執行緒安全的。

注:執行緒安全的通俗解釋 - 不管多個執行緒是怎樣的執行順序和優先順序,或是wait,sleep,join等控制方式,如果一個類在多執行緒訪問下運轉一切正常,並且訪問類不需要進行額外的同步處理或者協調,那麼我們就認為它是執行緒安全的。 執行緒安全的類應當封裝了所有必要的同步操作,呼叫者無需額外的同步。還有一點:無狀態的類永遠是執行緒安全的。

         在餓漢式的單例類中,其實有兩個狀態,單例未初始化和單例已經初始化。假設單例還未初始化,有兩個執行緒同時呼叫GetInstance方法,這時執行 m_pInstance == NULL 肯定為真,然後兩個執行緒都初始化一個單例,最後得到的指標並不是指向同一個地方,不滿足單例類的定義了,所以餓漢式的寫法會出現執行緒安全的問題!在多執行緒環境下,要對其進行修改。

多執行緒下的單例模式

        這裡要處理的是懶漢模式。

  1. class Singleton  
  2. {  
  3. private:  
  4.     static Singleton* m_instance;  
  5.     Singleton(){}  
  6. public:  
  7.     static Singleton* getInstance();  
  8. };  
  9. Singleton* Singleton::getInstance()  
  10. {  
  11.     if(NULL == m_instance)  
  12.     {  
  13.         Lock();//借用其它類來實現,如boost
  14.         if(NULL == m_instance)  
  15.         {  
  16.             m_instance = new Singleton;  
  17.         }  
  18.         UnLock();  
  19.     }  
  20.     return m_instance;  
  21. }  

使用double-check來保證thread safety.但是如果處理大量資料時,該鎖才成為嚴重的效能瓶頸。