模板單例實現

#模板單例實現
本文參考 [用模板實現單例模式](http://blog.csdn.net/jnu_simba/article/details/9398465 "用模板實現單例模式") ,而且在此基礎上作了適當的改進,使其支持更多功能。
    #ifndef Singleton_h__
    #define Singleton_h__
    template<class T>
    class Singletion
    {
    public:
    static T* GetInstance()
    {html

        static T m_Instance;linux

        return &m_Instance;
    }
安全

    private:
        Singletion(){};
        virtual ~Singletion(){};
        Singletion(const Singletion& ){};
        Singletion& operator=(const Singletion&){};
    };
    endif // Singleton_h__
    /*
具體使用方法以下:
    // 定義具體單例
       class CCenterImp
       {   
            public: // 構造函數要爲public,不能爲private和protect
                CCenterImp();     // 在構造函數中進行必要的初始化 
                ~CCenterImp ();  // 在析構函數中進行資源的釋放
            private:
               // Method
                void print();
               // Variable
            }
        }
       typedef Singletion<CCenterImp> CCenter;
      // 使用單例方法
      CCenter::GetInstance()->print();
注意,要以typedef的方法來定義單例具體名稱,具體單例CCenterImp的構造函數中初始化相關資源,在析構函數中釋放相關資源。
提出問題1:爲何不用雙重double的方式?多線程

回答:雙重double檢查外加互斥鎖的方式能夠在知足獲取要求,但何時釋放申請的內存很差控制,如下是改進版雙重檢測函數

        if (NULL == m_pInstance).net

   {線程

    // Lock相關指針

    if (NULL == m_pInstance)htm

    {對象

      T* temp = new T();  // 先new一個示例出來賦給臨時變量

      m_pInstance = temp;  // 在賦值給真實的變量

    }

   }

  return m_pInstance;

    若是不須要作成模板複用的話,能夠在此單例子中在增長一個內部嵌套靜態類,在其析構函數中釋放這裏申請的內存,但若是要作成模板類的話,這個內嵌類就無法實現了,所以放棄這種方式。

思考2:對於new出一個單例的方式,如何放置釋放處理函數。

回答: 參考網上的處理,有atexit函數,此函數是<stdlib.h>中的函數,能夠註冊一系列函數,用於在main函數退出後執行,在單例實例很少的狀況下,能夠考慮使用,但必定要注意它自身的限制,ISO C規定,一個進程在退出時,最多能夠註冊32個atexit函數,這些函數依次被exit函數所調用,若是要作成模板單例,供其餘模塊使用,就會有數量上的限制。 

思考3:對於new出來的實例,嘗試保存它自身以及相關析構函數,在系統銷燬時,統一調用一次。

回答:今天曾嘗試着將new出來的實例地址以及它對應的析構函數地址存放在map中,存放在另外一個單例中,等系統快要退出時統一顯示調用調用。這裏有2個問題,一個是多個不一樣的單例實例,只能以void*的形式保存其指針,在準備調用delete時,指針保存的類信息丟失了,沒法調用到其析構函數。 網上有利用匯編技巧的方法獲取析構函數的地址,參考連接:http://www.cnblogs.com/findumars/p/3746869.html, 我嘗試了下,執行到析構函數時發生崩潰了,嘗試了多種方式,仍是不行,此路不通。

思考4:單例類能夠在外部new一個出來或者在本地定義一個局部變量嗎?

回答:由於要構造一個單例類,因此這個單例類的構造函數必定不能是private或者是protect類型的,可是,這樣一類,就能夠直接在外部定義這樣一個類了,無論你是在單例的GetInstance裏面new出來,仍是 局部靜態變量出來,都須要構造函數。

思考5: 局部靜態變量的單例模式需不須要加鎖?

回答: 參考此篇文章,http://blog.csdn.net/yichigo/article/details/37878117, 若是加鎖保護,那麼之後每次調用GetInstance都會有鎖,若是不加鎖,若是構造函數中執行的時間較長,在多線程環境下,可能會這個變量構造到一半,就被另外一個線程拿過去用了。爲了平衡這兩種狀況,在程序中,對於此單例有可能有多線程訪問的狀況下,在程序啓動初始化時,顯示調用一個CCenter::GetInstance(),手動觸發初始化操做後,而後開啓多線程,這樣,既保證了在多線程工做時,此單例確定是初始化完成了,在獲取時,也不須要加鎖,快速直接。備註:局部靜態變量的線程安全性,在C++0x上的編譯器能夠保證,在linux平臺下的gcc編譯器能夠保證,在低版本的VC下不保證,所以,不要把這個變化的部分推給編譯器。在程序初始化時,顯示調用一次,確保構造完成後,再開啓多線程相關工做,咱們是能夠掌控這種狀況的。

思考6: 對於上文提出的,靜態局部變量的方式,有一點要注意,模板類裏面的構造、析構、拷貝構造、賦值要設置private,實現類裏面的構造要設置爲public,不能設置爲protect和private。這裏有一個小缺陷,用戶能夠定義或者new CCenterImp 類,但不能定義或者new CCenter類。整個單例對外表現的對象爲CCenter類,可是,CCenterImp只能定義在頭文件裏面,這樣,別人看到了就有誤用的可能,想來想去,也沒想到什麼好的方法,只能經過註釋文字說明來提示使用者,使用CCenter類,而不要直接使用CenterImp類。

    好了,關於單例的思考和討論彙總爲這麼多,之後在使用時,若是須要複用,就用上述提到的模板類,若是不須要複用,簡簡單單一個局部靜態變量加上手動初始化,可確保萬事大吉。

相關文章
相關標籤/搜索