最近一個朋友問我創建單實例的一個問題,他寫了一個C++單實例類CSingleton,其構造函數也是private類型。這個類有一個靜態函數:GetInstance,它返回單實例類對象的引用,只要用這個函數聲明實例便可以限制對象的復制:// 這一句編譯器通不過
CSingleton temp = CSingleton::GetInstance();
但是編譯器始終在上面這行代碼處受阻。這到底是為什麼呢?
其實,這個問題只要弄清楚編譯器處理單實例類的一些細節,問題便會迎刃而解。當編譯器碰到上面的這條語句時,它會去尋找類的拷貝構造函數,所謂的拷貝構造函數,就是一個署名為CSingleton(const CSingleton&)的構造函數。如果你沒有提供這個拷貝構造函數,那麼C++編譯器會為你提供一個默認的拷貝構造函數。這個默認的拷貝構造函數做的事情很簡單,只是將字節從一個對象靠到另一個對象。如果你想這個拷貝構造函數它干點別的什麼事情,你必須實現自己的拷貝構造函數。為了限制對象的復制,還必須使這個拷貝構造函數為private類型:
class CSingleton {
private:
// 私有類型的拷貝構造函數
CSingleton(const CSingleton& obj)
{ ASSERT(FALSE); } // should never happen
};
此外,你還必須實現默認的構造函數,也就是不帶參數的構造函數,如果沒有默認的構造函數,你將不能創建對象實例。默認的構造函數可以是private類型,也可以是protected類型,這要看你是不是想要從CSingleton派生其它的類。總而言之,要想正確創建單實例類,下面的這些原則或訣竅對你是有幫助的:
創建默認構造函數、拷貝構造函數和賦值操作符,並且它們的類型都為私有類型。
創建一個靜態函數GetInstance,用它來返回唯一的對象實例
下面是一個具體的例子代碼:
// 如果你編譯這個程序,會產生錯誤。參見main函數的注釋
//
class CSingleton {
public:
static CSingleton& GetInstance() {
static CSingleton theInstance; // 唯一實例
return theInstance;
}
protected:
// 需要默認的構造函數ctor
// 因為要創建派生類,ctor 為 protected 類型
CSingleton() { }
private:
CSingleton(const CSingleton& o) { }
CSingleton& operator=(const CSingleton& o) { }
};
void main()
{
// 由於構造函數是私有類型的,所以這幾行編譯通不過:
CSingleton x = CSingleton::GetInstance(); // error: private
// copy ctor!
CSingleton y = CSingleton::GetInstance(); // error: private
// copy ctor!
x = y; // error: private
// assignment!
// 編譯器看到這樣的實例聲明會更滿意一些:
CSingleton *p1 = CSingleton::GetInstance();
CSingleton *p2 = p1->GetInstance();
CSingleton & ref = * CSingleton::GetInstance();
}
本文配套源碼