程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 設計模式-單例模式

設計模式-單例模式

編輯:C++入門知識

單例模式:
確保程序的某一個類在運行的時候只生成一個對象,擁有對它類似於全局對象的訪問權限。

 


模式分析:
例如很多時候我們在寫程序的時候會有類似於DriverManager,CallManager等等這樣的管理類,它們主要的功能就是為了管理一堆相似的對象。對於這種管理類,一個程序一般只有一個對象,畢竟我們寫出管理類的目的就是為了便於管理,管理類的對象多了也就失去了管理的意義了,而且還有一點我們在程序中肯定會有很多地方需要直接訪問到這個對象,畢竟一個程序很多地方都需要通過管理類找到對應的Driver,對應的Call等等。

 


解決方案:
1,最簡單的我們可能定義一個全局對象
通過CallManager g_CallManager;創建一個全局對象,然後在需要地方通過extern CallManager g_CallManager;來獲取它的訪問權限。咋聽起來不錯,但是從第一次寫程序的時候,就會有很多經驗豐富的老手告訴我們全局對象的各種弊端,而且我們也沒有辦法確保對象只有一個,畢竟你可以在代碼中隨時通過CallManager myManager這樣類似代碼來創建對象。
2,單例模式
[cpp]
class CallManager 
    { 
    public: 
            static CallManager* GetInstance(){ 
                if(instance_ == NULL){//注意這個只有在第一次調用才會創建對象  
                    instance_ = new CallManager(); 
                }    
                return instance_; 
            } 
    private: 
         static CallManager* instance_;//注意這個為靜態私有對象  
         CallManager();//注意這個為私有  
    } 

class CallManager
 {
 public:
   static CallManager* GetInstance(){
    if(instance_ == NULL){//注意這個只有在第一次調用才會創建對象
     instance_ = new CallManager();
    } 
    return instance_;
   }
 private:
   static CallManager* instance_;//注意這個為靜態私有對象
   CallManager();//注意這個為私有
 }
[cpp]
static CallManager* CallManager::instance_ = NULL; 

 static CallManager* CallManager::instance_ = NULL;分析:
為了確保你不能在外部通過CallManager myManager等等其它類似的方案創建對象,其將構造函數定義為私有的,這樣就禁止了外部的顯式構造創建對象,根本編譯不過去。那你可能會問,那我還怎麼創建對象呢,畢竟我還需要一個對象。強大的GetInstance方法出現了,它通過訪問自己類的靜態成員instance_是否為空來決定是否創建對象,這樣可以給我們兩個保證:1,你可以創建對象,而且只會創建一個,2你可以在其他任何地方訪問到這個對象,通過GetInstance方法。如果你不是很懂靜態成員變量和靜態數據成員,建議找一些相關的文檔看一下,加深理解。靜態成員變量instance_給予我們一個類只有一個變量的限制,靜態成員函數GetInstance給予我們訪問私有靜態成員變量instance_的權利,同時還保證了只有一個對象被創建。
延伸:
1,如果你懂多線程,那麼你應該會發現上文中的代碼的問題,如果GetInstance方法同時被兩個地方調用,那麼就有可能同時new兩次,這個是不可取的。通常的解決辦法是GetInstance方法在系統初始化的時候就調用一次,確保對象的創建。我感覺這種方法很好,相當不建議用lock,因為這個方法用的地方會很多,加lock會很嚴重影響本來的效率。
2,new的CallManager什麼時候會被釋放?對於這種情況,我的建議是再創建一個靜態Destory方法,負責delete instance_。
[cpp]
static void DestroyInstance(){ 
    delete instance_; 
    instance_ = NULL; 
}    

  static void DestroyInstance(){
   delete instance_;
   instance_ = NULL;
  } 
3,調用DestroyInstance有點繁瑣,畢竟需要考慮在什麼地方調用,出現異常沒有調用等等一些問題,那倒不如把這個問題拋給系統吧。使用局部靜態對象。
[cpp]
public: 
    CallManager* GetInstance(){ 
        static CallManager localManager; 
        return &localManager; 
    } 

 public:
  CallManager* GetInstance(){
   static CallManager localManager;
   return &localManager;
  }通過這種局部靜態的使用既省去了DestroyInstance的調用,同時可以確保它一定會被釋放,還省去了instance_成員變量,何樂而不為呢?建議采用。
不過依然需要注意,GetInstance依然初始化需要調用一次,免去多線程的困擾,局部靜態對象只有在第一次調用的時候才會分配內存,可以通過在Callma的構造函數打印日志來驗證這個結論。


靜態變量版本:
[cpp]
#include <stdio.h>  
        class CallManager 
        { 
        public: 
            static CallManager* GetInstance(){ 
                static CallManager localManager; 
                return &localManager; 
            } 
            ~CallManager(){ 
                printf("CallManager Destructed...\n"); 
            } 
        private: 
            CallManager(){//注意這個為私有  
                printf("CallManager Constructed...\n"); 
            } 
        }; 

#include <stdio.h>
  class CallManager
  {
  public:
   static CallManager* GetInstance(){
    static CallManager localManager;
    return &localManager;
   }
   ~CallManager(){
    printf("CallManager Destructed...\n");
   }
  private:
   CallManager(){//注意這個為私有
    printf("CallManager Constructed...\n");
   }
  };請注意雖然我將函數的實現寫在了類的聲明裡面,但是請注意這並不是我本意。我是為了簡單,其實應該分成h和cpp文件的,這樣可以降低編譯依賴,而且默認內聯也不是我們想要的,具體的細節不再贅述。
main驅動程序:
[cpp]
#include <assert.h>  
        #include <CallManager.h>  
        int main(){ 
            CallManager* pCallManager  = CallManager::GetInstance(); 
            CallManager* pCallManager2 = CallManager::GetInstance(); 
            assert(pCallManager == pCallManager2);//確認是完全一樣  
            return 0; 
        } 

#include <assert.h>
  #include <CallManager.h>
  int main(){
   CallManager* pCallManager  = CallManager::GetInstance();
   CallManager* pCallManager2 = CallManager::GetInstance();
   assert(pCallManager == pCallManager2);//確認是完全一樣
   return 0;
  }
輸出結果:
[cpp]
CallManager Constructed... 
CallManager Destructed... 

  CallManager Constructed...
  CallManager Destructed...與我們所想一致,由初始化構建,只構建一次,在main結束之後被自動釋放。Wonderful。


後記:
不知道我的這種敘述方式會不會有點過於口語化了,我只想把我在學習它的時候的所得所思所獲說出來,跟大家探討一下,有些經驗希望能給大家帶來哪怕小小的幫助,描述的過程同樣是我再次回顧我的思路的過程,有錯誤歡迎指出,讓我也糾正我的錯誤,謝謝。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved