#模板單例實現
本文參考 [用模板實現單例模式](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類。
好了,關於單例的思考和討論彙總爲這麼多,之後在使用時,若是須要複用,就用上述提到的模板類,若是不須要複用,簡簡單單一個局部靜態變量加上手動初始化,可確保萬事大吉。