今天來看個... 設計模式吧
其實設計模式這東西比較是概念性的東西
主要是軟體設計一些比較通用的模式,套用在很多情況都適合
那麼這次呢,首先登場的是Singleton模式
什麼是Singleton模式呢,簡單講就是:整個程式裡面就只有他一個的意思
主要的精神就是整個程式的物件都透過這個singleton物件來得到一些資訊或是處理一些事情
像前東家的程式裡面的一些設定、管理連線的物件都是使用這種模式
因為設定是整個程式裡的很多物件都要使用到的,物件的網路連線都是一樣的,所以統一用一個物件來管理也很合理
那麼具體怎麼做呢,因為整個程式裡面就只能有他一個嘛
所以constructor就不能隨便被人家呼叫,不然大家各種呼叫就會出現好幾個了
因此... constructor必須是private的,另外賦值運算子(operator=)也是
那又有個問題了,constructor不能叫的話要怎麼建立和存取這個物件呢?
這個物件呢會提供一個public的函數來讓大家使用他,通常會叫做getInstance(),回傳值就是這個物件
在這個getInstance()會建立一個這個singleton物件,然後回傳這個物件
這邊提供一個範例(來自 https://stackoverflow.com/questions/1008019/c-singleton-design-pattern):
class S
{
public:
static S& getInstance()
{
static S instance; // Guaranteed to be destroyed.
// Instantiated on first use.
return instance;
}
private:
S() {} // Constructor? (the {} brackets) are needed here.
// C++ 03
// ========
// Don't forget to declare these two. You want to make sure they
// are unacceptable otherwise you may accidentally get copies of
// your singleton appearing.
S(S const&); // Don't Implement
void operator=(S const&); // Don't implement
// C++ 11
// =======
// We can use the better technique of deleting the methods
// we don't want.
public:
S(S const&) = delete; // do not use public constructor and operator=.
void operator=(S const&) = delete;
// Note: Scott Meyers mentions in his Effective Modern
// C++ book, that deleted functions should generally
// be public as it results in better error messages
// due to the compilers behavior to check accessibility
// before deleted status
};
當然上面那個例子是C++11之後可以使用的做法,在C++11之前的版本是不保證安全的
(先說結論,上面這個例子在C++11之後的版本是完全OK的)
上面那個是比較新的做法,較舊的作法可能會有一個private的member是m_instance
有時候考古也是必須的嘛,大概有以下兩種作法:
1. 可以選擇在一開始就建出m_instance(就是在member宣告的時候直接static S* m_instance = new S();,我覺得舊的作法這樣會節省很多麻煩...)
2. 或是在要使用的時候把m_instance建出來並回傳回去
大概會長成這樣:
static S* getInstance()
{
if (m_instance == NULL)
m_instance = new S();
return m_instance;
}
這樣的話如果不需要使用這個S,就不會初始化m_instance了,(當然前面用static也是一樣的效果)
只是這樣會有multi-thread的問題
為什麼會有multi-thread的問題呢,其實原因也很簡單
如果getInstance()在一開始被兩個thread同時呼叫了,這樣就會出現兩個singleton物件
等於說getInstance()裡面這段是一個critial section,必須要進行thread的同步,比如說加個mutex
所以大概可以改成這樣:
static S* getInstance()
{
m_mutex.lock();
if (m_instance == NULL)
m_instance = new S();
m_mutex.nulock();
return m_instance;
}
但這樣如果要時常存取這個m_intance,就會造成很多不必要的消耗
所以又有另一個方法叫雙重檢查上鎖,大概會長成這樣:
volatile S* m_instance; // 加上volatile,阻止編譯器最佳化這個變數,每次存取他的時候都會去檢查他的狀態
static S* getInstance()
{
if (m_instance == NULL) // 這樣每次都會去確實檢查instance是否為NULL,等於說只有第一次檢查的時候才會去同步
{
m_mutex.lock();
m_instance = new S();
m_mutex.nulock();
}
return m_instance;
}
好吧我覺得有點累... 我們還是用新的方法就好
舊的東西就稍微了解一下就行了XD
回到剛開始那邊,因為C++11保證了底下這件事:
§6.7 [stmt.dcl] p4
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
所以上面一開始那個例子是完全沒有multi-thread這些問題的
大概就介紹到這邊了~ 之後有想法再補充吧~
其實之前也看過滿多設計模式的,只是都快忘光了XD
有機會再來多寫幾篇~
留言列表