單例模式

定義

單例模式(Singleton Pattern,也稱為單件模式),使用最廣泛的設(shè)計(jì)模式之一。其意圖是保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn),該實(shí)例被所有程序模塊共享。

定義一個(gè)單例類,私有化它的構(gòu)造函數(shù),以防止外界創(chuàng)建單例類的對(duì)象;使用類的私有靜態(tài)指針變量指向類的唯一實(shí)例,并用一個(gè)公有的靜態(tài)方法獲取該實(shí)例。

單例模式類圖

代碼

懶漢模式

//頭文件  
class Singleton  
{  
    public:  
        static Singleton& Instance()                  //Instance()作為靜態(tài)成員函數(shù)提供里全局訪問點(diǎn)  
        {  
            if(ps == NULL)                        //如果還未實(shí)例化,即可實(shí)例話,反之提供實(shí)例的引用  
                ps = new Singleton;  
            return *ps;                           //返回指針的話可能會(huì)誤被 delete,返回引用安全一點(diǎn)  
        }  
  
    private:  
        Singleton();                                  //這里將構(gòu)造,析構(gòu),拷貝構(gòu)造,賦值函數(shù)設(shè)為私有,杜絕了生成新例  
        ~Singleton();  
        Singleton(const Singleton&);  
        Singleton& operator=(const Singleton&);  
  
        static Singleton* ps;  
};  
  
//源文件  
Singleton* Singleton::ps = NULL;

這種方法的好處在于直到 Instance() 被訪問,才會(huì)生成實(shí)例,這種特性被稱為延遲初始化(Lazy Initialization),這在一些初始化時(shí)消耗較大的情況有很大優(yōu)勢(shì)。
Lazy Singleton不是線程安全的,比如現(xiàn)在有線程A和線程B,都通過了 ps == NULL 的判斷,那么線程A和B都會(huì)創(chuàng)建新實(shí)例。單例模式保證生成唯一實(shí)例的規(guī)則被打破了。
改進(jìn)的線程安全的懶漢模式
可以通過加鎖來保護(hù)單例初始化這一過程,雙檢測(cè)鎖模式就是在懶漢模式的基礎(chǔ)上稍作修改得到:

//頭文件  
class Singleton  
{  
    public:  
        static Singleton& Instance()                //Instance()作為靜態(tài)成員函數(shù)提供里全局訪問點(diǎn)  
        {  
            if(ps == NULL)  
            {     
                Lock();             //上鎖  
                if(ps == NULL)          //如果還未實(shí)例化,即可實(shí)例話,反之提供實(shí)例的引用  
                    ps = new Singleton;  
                Unlock();           //解鎖  
            }  
            return *ps;                             //返回指針的話可能會(huì)誤被 delete,返回引用安全一點(diǎn)  
        }     
  
    private:  
        Singleton();                                    //這里將構(gòu)造,析構(gòu),拷貝構(gòu)造,賦值函數(shù)設(shè)為私有,杜絕了生成新例  
        ~Singleton();  
        Singleton(const Singleton&);  
        Singleton& operator=(const Singleton&);  
  
        static Singleton* ps;  
};  
  
//源文件  
Singleton* Singleton::ps = NULL;  

上鎖和解鎖僅用于說明,實(shí)際應(yīng)用中可以使用互斥鎖,單一信號(hào)量等方法去實(shí)現(xiàn)。理論上問題解決了,但是在實(shí)踐中有很多坑,如指令重排、多核處理器等問題讓DCLP實(shí)現(xiàn)起來比較復(fù)雜比如需要使用內(nèi)存屏障

餓漢模式

//頭文件  
class Singleton  
{  
    public:  
        static Singleton& Instance()                  //Instance()作為靜態(tài)成員函數(shù)提供里全局訪問點(diǎn)  
        {  
            return instance;  
        }  
  
    private:  
        Singleton();                                  //這里將構(gòu)造,析構(gòu),拷貝構(gòu)造,賦值函數(shù)設(shè)為私有,杜絕了生成新例  
        ~Singleton();  
        Singleton(const Singleton&);  
        Singleton& operator=(const Singleton&);  
  
        static Singleton instance;  
};  
  
//源文件  
Singleton Singleton::instance; 

與懶漢模式相反,實(shí)例化是在初始化階段執(zhí)行的,所以沒有線程安全的問題,但是潛在問題在于no-local static對(duì)象(函數(shù)外的static對(duì)象)在不同編譯單元(可理解為cpp文件和其包含的頭文件)中的初始化順序是未定義的。如果在初始化完成之前調(diào)用 Instance()方法會(huì)返回一個(gè)未定義的實(shí)例。

優(yōu)雅的單例模式實(shí)現(xiàn)

//頭文件  
class Singleton  
{  
    public:  
        static Singleton& Instance()                  //Instance()作為靜態(tài)成員函數(shù)提供里全局訪問點(diǎn)  
        {  
            static Singleton instance;  
            return instance;  
        }  
  
    private:  
        Singleton();                                  //這里將構(gòu)造,析構(gòu),拷貝構(gòu)造,賦值函數(shù)設(shè)為私有,杜絕了生成新例  
        ~Singleton();  
        Singleton(const Singleton&);  
        Singleton& operator=(const Singleton&);       
}; 

使用local static對(duì)象(函數(shù)內(nèi)的static對(duì)象)。當(dāng)?shù)谝淮卧L問 Instance() 方法時(shí)才創(chuàng)建實(shí)例。該實(shí)現(xiàn)是線程安全的

借助pthread_once() 函數(shù)實(shí)現(xiàn)單例模式

在多線程編程環(huán)境下,盡管 pthread_once() 調(diào)用會(huì)出現(xiàn)在多個(gè)線程中,init_routine()函數(shù)僅執(zhí)行一次,pthread_once是很適合用來實(shí)現(xiàn)線程安全單例。(pthread_once 在一個(gè)進(jìn)程里只會(huì)執(zhí)行一次,其實(shí)現(xiàn)方式使用的就是互斥鎖+條件變量的方法)

//頭文件  
pthread_once_t once = PTHREAD_ONCE_INIT;  
class Singleton  
{  
    public:  
        static Singleton& Instance()                  //Instance()作為靜態(tài)成員函數(shù)提供一次實(shí)例化以及全局訪問點(diǎn)  
        {  
            pthread_once(&once, &Init);  
            return *ps;  
        }  
  
        static void Init()  
        {  
            ps = new Singleton;  
        }  
  
    private:  
        Singleton();  
        ~Singleton();  
        Singleton(const Singleton&);  
        Singleton& operator=(const Singleton&);  
  
        static Singleton* ps;  
};  
//源文件  
Singleton* Singleton::ps = NULL;  

單例模式的實(shí)現(xiàn)方法很多,要完成一個(gè)完美的實(shí)現(xiàn)很難,代碼也會(huì)很復(fù)雜,但是掌握基礎(chǔ)的實(shí)現(xiàn)還是很必要的,文中提到的優(yōu)雅的單例模式借助pthread_once() 函數(shù)實(shí)現(xiàn)單例模式是相對(duì)比較完善的方法。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容