close

今天來看個... 設計模式吧

其實設計模式這東西比較是概念性的東西

主要是軟體設計一些比較通用的模式,套用在很多情況都適合

 

那麼這次呢,首先登場的是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

有機會再來多寫幾篇~

arrow
arrow
    全站熱搜

    頁頁頁六滴 發表在 痞客邦 留言(0) 人氣()