某不知名處的一群土著人過著和諧快樂的日子。直到一個玻璃瓶從天而降打破平靜。這瓶子用途極廣:有人砸地瓜、有人吹口哨、有人擀蛇皮……大夥兒離不開它,搶著用。為恢復和諧,一位土著決定把瓶子扔掉……
定義瓶子類,所有方法皆為 static,不涉及屬性:
class CGlassBottle {
private:
CGlassBottle();
public:
virtual ~CGlassBottle();
static BOOL OnKnock(); // 砸
static BOOL OnBlow(); // 吹
static BOOL OnRoll(); // 擀
protected:
static BOOL s_bUsed;
};
土著 A 只砸地瓜,土著 B 只吹口哨:
class A { public: void UseBottle() { CGlassBottle::OnKnock(); } };
class B { public: void UseBottle() { CGlassBottle::OnBlow(); } };
執行結果符合預期,這就是「類單例」——封裝相似功能塊,盡量不涉及屬性。如進制轉換工具類:
class CNumFormat {
private: CNumFormat();
public:
static CString From2TO10(LPCTSTR);
static CString From2TO16(LPCTSTR);
...
《深入淺出設計模式》定義:確保一個類別只有一個實例,並提供全域存取點。改進瓶子類:
class CGlassBottle {
private:
CGlassBottle();
public:
virtual ~CGlassBottle();
bool OnKnock(); bool OnBlow(); bool OnRoll();
public:
static CGlassBottle s_GlassBottle; // 靜態實例
protected:
bool m_bUsed;
};
土著呼叫改為 CGlassBottle::s_GlassBottle.OnKnock()。
不想暴露實例,改用 GetInstance():
class CGlassBottle {
private:
CGlassBottle();
static CGlassBottle s_GlassBottle;
public:
virtual ~CGlassBottle();
bool OnKnock(); bool OnBlow(); bool OnRoll();
static CGlassBottle* GetInstance() { return &s_GlassBottle; }
protected:
bool m_bUsed;
};
呼叫:CGlassBottle::GetInstance()->OnKnock()。
利用函數靜態區域變數,首次呼叫才建構:
static CGlassBottle* GetInstance() {
static CGlassBottle s_GlassBottle; // 延遲建構
return &s_GlassBottle;
}
筆者偏好此寫法,單執行緒下無問題,多執行緒安全性視編譯器而定 (C++11 後保證 thread-safe)。
覺得 CGlassBottle::GetInstance()->OnKnock() 囉嗦?定義宏:
#define GetGlassBottle() CGlassBottle::GetInstance()
GetGlassBottle()->OnKnock();
將 GetInstance 設為私有,授權友元函數 GetGlassBottle():
class CGlassBottle {
private:
CGlassBottle();
friend CGlassBottle* GetGlassBottle();
static CGlassBottle* GetInstance() {
static CGlassBottle s_GlassBottle;
return &s_GlassBottle;
}
protected:
bool m_bUsed;
};
inline CGlassBottle* GetGlassBottle() { return CGlassBottle::GetInstance(); }
呼叫變為 GetGlassBottle()->OnKnock(),舒服許多。
連類別靜態函數都省掉,直接在全域內聯函數裡放靜態實例:
class CGlassBottle {
private:
CGlassBottle();
friend CGlassBottle* GetGlassBottle();
protected:
bool m_bUsed;
public:
bool OnKnock(); bool OnBlow(); bool OnRoll();
virtual ~CGlassBottle();
};
inline CGlassBottle* GetGlassBottle() {
static CGlassBottle s_GlassBottle;
return &s_GlassBottle;
}
用法同前,簡潔且單執行緒下從未出問題。