全國(guó)咨詢(xún)/投訴熱線:400-618-4000

首頁(yè)技術(shù)文章正文

單例模式(C++)

更新時(shí)間:2018-12-14 來(lái)源:黑馬程序員技術(shù)社區(qū) 瀏覽量:

單例模式是設(shè)計(jì)模式中最簡(jiǎn)單的形式之一。這一模式的目的是使得類(lèi)的一個(gè)對(duì)象成為系統(tǒng)中的唯一實(shí)例。要實(shí)現(xiàn)這一點(diǎn),可以從客戶(hù)端對(duì)其進(jìn)行實(shí)例化開(kāi)始。因此需要用一種只允許生成對(duì)象類(lèi)的唯一實(shí)例的機(jī)制,“阻止”所有想要生成對(duì)象的訪問(wèn)。使用工廠方法來(lái)限制實(shí)例化過(guò)程。這個(gè)方法應(yīng)該是靜態(tài)方法(類(lèi)方法),因?yàn)樽岊?lèi)的實(shí)例去生成另一個(gè)唯一實(shí)例毫無(wú)意義。<來(lái)自百度百科>

簡(jiǎn)單地說(shuō),單例模式表示的含義是某一個(gè)類(lèi), 在一個(gè)進(jìn)程中只能有唯一的一個(gè)對(duì)象(實(shí)例), 并且在語(yǔ)法角度上進(jìn)行制約.

單例模式要點(diǎn)

某個(gè)類(lèi)只能有一個(gè)實(shí)例

單例模式的類(lèi)只提供私有的構(gòu)造函數(shù),禁止使用拷貝構(gòu)造函數(shù)或者賦值運(yùn)算符來(lái)創(chuàng)建新的對(duì)象


它必須自行創(chuàng)建這個(gè)實(shí)例

類(lèi)定義中,含有一個(gè)該類(lèi)的靜態(tài)私有對(duì)象,既保證所有的實(shí)例只有一個(gè)成員變量,那么等同于這個(gè)類(lèi)只有一個(gè)對(duì)象


他必須自行向整個(gè)系統(tǒng)提供這個(gè)實(shí)例

該類(lèi)提供一個(gè)靜態(tài)的公有的函數(shù)用于創(chuàng)建或獲取它本身的靜態(tài)私有對(duì)象



單例模式用途

剛開(kāi)始接觸單例模式的時(shí)候,并不知道單例模式的用途是什么,但是其實(shí)使用單例模式的地方還是很多的,比如:

每臺(tái)計(jì)算機(jī)可以有若干個(gè)打印機(jī),但只能有一個(gè)Printer Spooler, 以避免兩個(gè)打印作業(yè)同時(shí)輸出到打印機(jī)中。

一個(gè)系統(tǒng)只能有一個(gè)窗口管理器或文件系統(tǒng);

一個(gè)系統(tǒng)只能有一個(gè)計(jì)時(shí)工具或ID(序號(hào))生成器;


如在Windows中就只能打開(kāi)一個(gè)任務(wù)管理器。如果不使用機(jī)制對(duì)窗口對(duì)象進(jìn)行唯一化,將彈出多個(gè)窗口,如果這些窗口顯示的內(nèi)容完全一致,則是重復(fù)對(duì)象,浪費(fèi)內(nèi)存資源;如果這些窗口顯示的內(nèi)容不一致,則意味著在某一瞬間系統(tǒng)有多個(gè)狀態(tài),與實(shí)際不符,也會(huì)給用戶(hù)帶來(lái)誤解,不知道哪一個(gè)才是真實(shí)的狀態(tài)。因此有時(shí)確保系統(tǒng)中某個(gè)對(duì)象的唯一性即一個(gè)類(lèi)只能有一個(gè)實(shí)例非常重要。

所以: 當(dāng)實(shí)例存在多個(gè)會(huì)引起程序邏輯錯(cuò)誤的時(shí)候,請(qǐng)使用單例模式

單例模式的優(yōu)點(diǎn)

單例模式會(huì)阻止其他對(duì)象實(shí)例化其自己的單例對(duì)象的副本,從而確保所有對(duì)象都訪問(wèn)唯一的實(shí)體

因?yàn)轭?lèi)控制了實(shí)例化過(guò)程,所以類(lèi)可以靈活更改實(shí)例化過(guò)程


單例模式缺點(diǎn)

雖然數(shù)量很少,但是如果每次對(duì)象請(qǐng)求引用的時(shí)候都要檢查是否存在類(lèi)的實(shí)例,仍需要一些開(kāi)銷(xiāo);

懶漢模式以時(shí)間換空間;

餓漢模式以空間換時(shí)間;


單例模式常見(jiàn)兩種形式餓漢模式

餓漢模式就是一開(kāi)始就將資源加再進(jìn)來(lái),可以說(shuō)是一空間換時(shí)間的一種做法,每次使用的時(shí)候直接返回就好了



//餓漢模式



class Singleton



{



protected:



    Singleton(){} // 設(shè)置為保護(hù)便于被繼承



private:



    Singleton(const Singleton& s) = delete; // 防拷貝(注:delete是c++11中的語(yǔ)法)



    Singleton* operator=(const Singleton& s) = delete; // 防賦值



private:



    static Singleton* p;



public:



    static Singleton* getInstance();



};







Singleton* Singleton::p = new Singleton(); // 第一次常見(jiàn)類(lèi)就給分配資源







Singleton* Singleton::getInstance()



{



    return p;



}


注意: 餓漢模式不存在線程安全的問(wèn)題

懶漢模式

懶漢模式就是等到實(shí)例化對(duì)象的時(shí)候才將資源加載進(jìn)來(lái),可以說(shuō)是以時(shí)間換空間的做法

經(jīng)典懶漢模式(線程不安全)

class Singleton



{



protected:



    Singleton(){} //將構(gòu)造函數(shù)設(shè)置為保護(hù),便于其他類(lèi)繼承



private:



    Singleton(const Singleton& s) = delete; // 實(shí)現(xiàn)防拷貝



    Singleton& operator=(const Singleton& s) = delete; // 實(shí)現(xiàn)防賦值



public:



    static Singleton* getInstance();



private:



    static Singleton* p;



};







Singleton* Singleton::p = NULL;



Singleton* Singleton::getInstance()



{



    if(p == NULL)



    {// 在第一次調(diào)用的時(shí)候才去new一個(gè)對(duì)象



        p = new Singleton();



    }



    return p;



}


線程安全的懶漢模式

class Singleton



{



protected:



    Singleton()



    {// 初始化互斥鎖



        pthread_mutex_init(&lock_,NULL);



    }



private:



    Singleton(const Singleton& s) = delete; // 實(shí)現(xiàn)防拷貝



    Singleton& operator=(const Singleton& s) = delete; // 實(shí)現(xiàn)防賦值



public:



    static pthread_mutex_t lock_;



    static Singleton* getInstance();



private:



    static Singleton* p; //加volatile防止編譯器過(guò)度優(yōu)化



};







pthread_mutex_t Singleton::lock_;



Singleton* Singleton::p = NULL;



Singleton* Singleton::getInstance()



{



    if(p == NULL)



    {



        pthread_mutex_lock(&lock_);



        if(p == NULL)



        {



            p = new Singleton();



        }



        pthread_mutex_unlock(&lock_);



    }



    return p;



}


內(nèi)部靜態(tài)變量實(shí)現(xiàn)懶漢模式

class Singleton



{



protected:



    Singleton()



    {



        pthread_mutex_init(&lock_,NULL);



    }



private:



    Singleton(const Singleton& s) = delete;



    Singleton* operator=(const Singleton& s) = delete;



public:



    static pthread_mutex_t lock_;



    static Singleton* getInstance();



};







pthread_mutex_t Singleton::lock_;



Singleton* Singleton::getInstance()



{



    pthread_mutex_lock(&lock_);



    static Singleton obj;



    pthread_mutex_unlock(&lock_);



    return &obj;



}


特點(diǎn)與選擇

由于要進(jìn)行線程同步,所以在訪問(wèn)量比較大,或者可能訪問(wèn)的線程比較多時(shí),采用餓漢實(shí)現(xiàn),可以實(shí)現(xiàn)更好的性能。這是以空間換時(shí)間。

在訪問(wèn)量較小時(shí),采用懶漢實(shí)現(xiàn)。這是以時(shí)間換空間。

面試的時(shí)候,就寫(xiě)?zhàn)I漢模式吧,畢竟簡(jiǎn)單

作者:黑馬程序員C++培訓(xùn)學(xué)院    
首發(fā):http://c.itheima.com/?v2
分享到:
在線咨詢(xún) 我要報(bào)名
和我們?cè)诰€交談!