1. 程式人生 > >設計模式之單例模式的幾種寫法

設計模式之單例模式的幾種寫法

前言

經過了前面幾次的面試,我發現面試官對設計模式方面的提問頻率是非常高的,所以學習設計模式是一件刻不容緩的事情,今天我們就來說一下設計模式中的單例模式吧。

什麼是設計模式?

設計模式的原則

什麼是單例模式

顧名思義,單例模式的含義就和它的名字一樣,就是一個型別只能例項化一個物件出來,並且這個物件對整個系統來說是可見的,也就是說我們要提供一個全域性訪問點,向整個系統提供這個唯一的例項。

單例模式的實現(C++)

好了,經過了前面的敘述,我們現在可以寫下程式碼了,這裡我們用C++來實現單例模式,語言不重要,思路才是關鍵。

class Singleton1
{
public
: static Singleton1* GetInstance() { if (single == NULL) { single = new Singleton(); } return single; } private: Singleton1() //注意建構函式是私有的 {} private: static Singleton1* single; }; Singleton1* Singleton1:: single=NULL;

這段程式碼實現了單例模式的關鍵點有兩個:
1.保護


我們將類的建構函式宣告成非公有的,這樣就可以防止通過類的成員函式來在任何地方例項化物件,當然,我這裡寫的不是很嚴格,我們不光要對建構函式進行保護,我們還應該對拷貝建構函式以及賦值運算子過載等類的預設函式也提供保護。

2.類的成員變數申明為static型別:
這一點是很關鍵的,因為單例模式的設計中有一個必要的原則就是類的例項物件只能有一個,而且類的實體對整個系統都要提供介面來進行訪問,使用靜態變數可以滿足上面的要求,因為靜態變數只要物件已建立它就一直存在,知道程式結束完才被銷燬。

大家應該注意到我寫的類名為Singleton1了吧,因為僅僅這樣實現是不夠的,還需要考慮一些其他的因素,例如多執行緒環境時執行上面的程式碼就會出現問題,例如當第一個執行緒執行到if (single == NULL)

的時候被系統切了出去,此時第二個執行緒也執行到了這句話,但是此時物件沒有被建立,所以single的狀態也就沒有發生變化,執行緒2會建立一個物件,當回到執行緒1時,由於判斷條件已經執行過了,所以執行緒1也會建立這個物件,這就導致在一個程序中有兩個物件,違背了單例模式的設計原則。

上述問題的發生是因為執行緒安全性的問題所導致的,我們可以引入互斥鎖來解決這個問題,因此在多執行緒的環境下,單例模式的程式碼可以這樣來寫:


std::mutex g_lock;


class Singleton
{
public:
    static Singleton* GetInstance()
    {
        g_lock.lock();
        if (single == NULL)
        {
            single = new Singleton();
        }
        g_lock.unlock();
        return single;
    }
private:
    Singleton()     //注意建構函式是私有的
    {}
private:
    static Singleton* single;
};
Singleton* Singleton:: single=NULL;

我們通過引入互斥鎖來維護了上面提到的執行緒安全之間的問題,但是各個執行緒反覆的申請鎖是很影響效能的,所以我們還可以在此基礎上再增加一層判斷條件,當single為NULL時才允許申請鎖資源。

std::mutex g_lock;

class Singleton
{
public:
    static Singleton* GetInstance()
    {
        if (single == NULL)
        {
            g_lock.lock();
            if (single == NULL)
            {
                single = new Singleton();
            }
            g_lock.unlock();
        }
        return single;
    }
private:
    Singleton()     //注意建構函式是私有的
    {}
private:
    static Singleton* single;
};
Singleton* Singleton:: single=NULL;

好了,程式碼在這裡就基本實現完了,我們現在可以再來回顧一下單例模式。

單例模式和全域性變數的區別

通過觀察前面的程式碼,我們可以發現單例模式的作用其實就像全域性變數一樣,可以說是全部變數的替代品,它擁有全部變數的特性,例如生命週期等。但此外,它還有一些全部變數所不具有的特性,單例模式中,同一個型別的例項物件只能有一個,此外,單例模式還提供了延遲初始化,以提高程式的效能。