1. 程式人生 > >Head First 設計模式(C++實現):單例模式:Singleton

Head First 設計模式(C++實現):單例模式:Singleton

單例模式:確保一個類只有一個例項,並提供一個全域性訪問點

1 .經典單例模式實現

  我們都很清楚一個簡單的單例模式該怎樣去實現:建構函式宣告為private或protect防止被外部函式例項化,內部儲存一個private static的類指標儲存唯一的例項,例項的動作由一個public的類方法代勞,該方法也返回單例類唯一的例項。

  程式碼如下: 

class singleton

{
protected:
    singleton(){}
private:
    static singleton* p;
public:
    static singleton* instance();
};
singleton* singleton::p = NULL;
singleton* singleton::instance()
{
    if (p == NULL)
        p = new singleton();
    return p;
}


  這是一個很棒的實現,簡單易懂。但這是一個完美的實現嗎?不!該方法是執行緒不安全的,考慮兩個執行緒同時首次呼叫instance方法且同時檢測到p是NULL值,則兩個執行緒會同時構造一個例項給p,這是嚴重的錯誤!同時,這也不是單例的唯一實現!


2 懶漢與餓漢

  單例大約有兩種實現方法:懶漢與餓漢。懶漢在Head First設計模式中稱為“延遲例項化”,餓漢稱為“急切例項化”。

    • 懶漢:故名思義,不到萬不得已就不會去例項化類,也就是說在第一次用到類例項的時候才會去例項化,所以上邊的經典方法被歸為懶漢實現;
    • 餓漢:餓了肯定要飢不擇食。所以在單例類定義的時候就進行例項化。

  特點與選擇:

    • 由於要進行執行緒同步,所以在訪問量比較大,或者可能訪問的執行緒比較多時,採用餓漢實現,可以實現更好的效能。這是以空間換時間。
    • 在訪問量較小時,採用懶漢實現。這是以時間換空間。

3 執行緒安全的懶漢實現(延遲建立例項

  執行緒不安全,怎麼辦呢?最直觀的方法:加鎖。

方法1:加鎖的經典懶漢實現(雙重檢查加鎖):

class singleton
{
protected:
    singleton()
    {
        pthread_mutex_init(&mutex);
    }
private:
    static singleton* p;
public:
    static pthread_mutex_t mutex;
    static singleton* initance();
};

pthread_mutex_t singleton::mutex;
singleton* singleton::p = NULL;
singleton* singleton::initance()
{
    if (p == NULL)//檢查例項,如果不存在,進入同步區塊
    {
        //注意:只有一次徹底執行這裡的程式碼
        pthread_mutex_lock(&mutex);
        if (p == NULL)// 進入區塊後在檢查一次,如果仍是NULL,才建立例項
            p = new singleton();
        pthread_mutex_unlock(&mutex);
    }
    return p;
}

方法2:內部靜態變數的懶漢實現

  此方法也很容易實現,在instance函式裡定義一個靜態的例項,也可以保證擁有唯一例項,在返回時只需要返回其指標就可以了。推薦這種實現方法,真得非常簡單。    

class singleton
{
protected:
    singleton()
    {
        pthread_mutex_init(&mutex);
    }
public:
    static pthread_mutex_t mutex;
    static singleton* initance();
    int a;
};

pthread_mutex_t singleton::mutex;
singleton* singleton::initance()
{
    pthread_mutex_lock(&mutex);
    static singleton obj;
    pthread_mutex_unlock(&mutex);
    return &obj;
}

 4 餓漢實現 (急切建立例項

  為什麼我不講“執行緒安全的餓漢實現”?因為餓漢實現本來就是執行緒安全的,不用加鎖。為啥?自己想!

class singleton
{
protected:
    singleton(){}
private:
    static singleton* p;
public:
    static singleton* initance();
};
singleton* singleton::p = new singleton;
singleton* singleton::initance()
{
    return p;//已經有例項化了,直接使用它
}

在靜態初始化器中建立單例,保證了執行緒安全

參考書籍:《Head First設計模式》