單例模式(Singleton Pattern)

實現單例模式的思路是:一個類別只有一個實例,並提供全域點存取此實例。

有些物件只需要一個實例,比如設定和登入的物件、和驅動程式溝通的物件、執行緒池等,單例模式可以在任何地方存取這個實體,像全域變數一樣方便,且需要時才建立物件,不像全域變數程式一開始就建好物件,造成可能的資源浪費。以下我們示範單例模式的使用,由於一次只會有一個實例產生,所以兩個instance有相同的位址,以下為程式碼。

#include <iostream>
#include <string>
using namespace std; 

class Singleton {
private:
    static Singleton *m_instance;
    Singleton(){}
public:
    static Singleton *getInstance(){
        if(m_instance==NULL){
            m_instance = new Singleton();
        }
        return m_instance;
    }
};
Singleton* Singleton::m_instance = NULL;

int main(){
    Singleton *instance1 = Singleton::getInstance();
    Singleton *instance2 = Singleton::getInstance();
    int addr1 = (int)instance1;
    int addr2 = (int)instance2;
    delete instance1;
    return 0;
}

單例模式在多執行緒的應用場合下要小心,當唯一實例尚未創建時,如果有兩個執行緒同時調用創建方法,可能各自創建了一個實例,這樣就有兩個實例被構造出來,違反了單例模式的原則,有以下三種解決方式:
1.對getInstance()這個函式同步化,不過會造成程式執行效率降低,如果需要頻繁運作此函式,可能需要重新考慮。
2.率先建立實例,而不用拖延實體化的作法,如以下所示:

class Singleton {
private:
    static Singleton *m_instance = new Singleton();
    Singleton(){}
public:
    static Singleton *getInstance(){
        return m_instance;
    }
};

3.利用雙重檢查上鎖,在getInstance()中減少使用同步化,檢查實例是否已經建立,沒建立的話才進行同步化,如此一來只有第一次會同步化,如以下所示:

class Singleton {
private:
    //volatile不會對m_instance進行編譯器最佳化,確保多執行緒處理m_instance是正確的
    volatile static Singleton *m_instance;
    Singleton(){}
public:
    static Singleton *getInstance(){
        if(m_instance==NULL){
            //下面進行同步化
            m_instance = new Singleton();
        }
        return m_instance;
    }
};