有時(shí)候?qū)崿F(xiàn)了一個(gè)類,但只需要?jiǎng)?chuàng)建出一個(gè)實(shí)例化的對(duì)象就能完成需求,如果有太多的對(duì)象不僅浪費(fèi)內(nèi)存空間也會(huì)使得代碼數(shù)據(jù)不那么好維護(hù),因此會(huì)需要設(shè)計(jì)出一個(gè)只能生成一個(gè)實(shí)例的類;
在冠縣等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供做網(wǎng)站、成都網(wǎng)站設(shè)計(jì) 網(wǎng)站設(shè)計(jì)制作按需規(guī)劃網(wǎng)站,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),全網(wǎng)整合營(yíng)銷推廣,外貿(mào)營(yíng)銷網(wǎng)站建設(shè),冠縣網(wǎng)站建設(shè)費(fèi)用合理。
首先,要使得這個(gè)類只能實(shí)例化出一個(gè)對(duì)象,那么它的構(gòu)造函數(shù)肯定不能夠被外部隨意調(diào)用,因此應(yīng)該將類的構(gòu)造函數(shù)的訪問(wèn)限定符設(shè)定為私有的,但是這樣的話,如何獲得唯一一個(gè)的實(shí)例化對(duì)象呢?可以設(shè)計(jì)一個(gè)成員函數(shù),這個(gè)成員函數(shù)只能分配出唯一一個(gè)該類對(duì)象的內(nèi)存空間,并且返回指向這塊空間的指針給申請(qǐng)的對(duì)象,因此,可以將類設(shè)計(jì)為如下:
#includeusing namespace std; class Singleton { private: Singleton() {} Singleton(Singleton& s); Singleton& operator=(Singleton& s); public: static Singleton*& GetInstance() { if(_instance == NULL) { _instance = new Singleton(); } return _instance; } ~Singleton() { if(_instance != NULL) delete _instance; } private: static Singleton *_instance; }; Singleton* Singleton::_instance = NULL;
上面的栗子中將構(gòu)造函數(shù)、拷貝構(gòu)造和賦值運(yùn)算符的重載函數(shù)的訪問(wèn)限定符置為私有的,外界不能通過(guò)這些函數(shù)實(shí)例化出對(duì)象;然后設(shè)計(jì)了一個(gè)靜態(tài)成員函數(shù),這里值得一提的是,靜態(tài)的成員函數(shù)沒(méi)有隱含的this指針,因此外界可以通過(guò)類名加域的訪問(wèn)符號(hào)::來(lái)調(diào)用靜態(tài)的成員函數(shù);
類的成員變量只定義了一個(gè)指向類對(duì)象的一個(gè)_instance指針,靜態(tài)的成員函數(shù)可以在類外部初始化,先將其初始化為NULL,當(dāng)?shù)谝淮握{(diào)用這個(gè)GetInstance函數(shù)的時(shí)候,_instance為NULL,因此為其分配一個(gè)類對(duì)象的空間并將其返回;但是當(dāng)?shù)诙握{(diào)用這個(gè)函數(shù)的時(shí)候,_instance已經(jīng)有了值并不會(huì)再分配空間而是返回已經(jīng)存在的實(shí)例化出的對(duì)象,因此無(wú)論怎么調(diào)用GetInstance函數(shù)都只會(huì)返回同一塊地址空;
但是上面的函數(shù)在單線程環(huán)境下運(yùn)行是沒(méi)有問(wèn)題的,當(dāng)有多個(gè)線程并發(fā)訪問(wèn)這個(gè)函數(shù)的時(shí)候,如果兩個(gè)或多個(gè)線程同時(shí)拿到了初始化為NULL的_instance的值要進(jìn)行判斷的時(shí)候,有可能都會(huì)判斷成功,也就是會(huì)new出來(lái)兩塊不同的地址空間,這樣就不符合設(shè)計(jì)的初衷了,因此在多線程運(yùn)行環(huán)境下,GetInstance函數(shù)中的代碼就會(huì)成為臨界區(qū),也就是要有互斥的關(guān)系,可以為其加上mutex互斥鎖:
static Singleton*& GetInstance() { pthread_mutex_lock(&lock); if(_instance == NULL) { _instance = new Singleton(); } pthread_mutex_unlock(&lock); return _instance; }
可是上面的代碼還是存在一些效率的問(wèn)題,如果一個(gè)線程最開(kāi)始獲得了鎖并且成功new出了空間,那么之后的線程每一次進(jìn)到函數(shù)GetInstance里面都要爭(zhēng)奪一下鎖資源并且再依次判斷,所以,可以在加鎖之前就先進(jìn)行一次判斷,如果_instance不為空,后面就沒(méi)有必要競(jìng)爭(zhēng)鎖資源再進(jìn)行判斷了,所以代碼可以優(yōu)化為如下:
static Singleton*& GetInstance() { if(_instance == NULL) { pthread_mutex_lock(&lock); if(_instance == NULL) { _instance = new Singleton(); } pthread_mutex_unlock(&lock); } return _instance; }
其實(shí)除了上面所給出的解法,還有另外一種簡(jiǎn)單粗暴的設(shè)計(jì)方式,那就是直接在給靜態(tài)成員變量_instance初始化的時(shí)候就初始化為new出來(lái)的一個(gè)類的實(shí)例化對(duì)象,之后每一次調(diào)用GetInstance函數(shù)獲取_instance的值的時(shí)候就直接返回:
class Singleton { private: Singleton() {} Singleton(Singleton& s); Singleton& operator=(Singleton& s); public: static Singleton*& GetInstance() { return _instance; } private: static Singleton *_instance; }; Singleton* Singleton::_instance = new Singleton();
上面一次性就將對(duì)象空間給開(kāi)辟出來(lái)每次不用判斷就直接返回,這種方式被稱為餓漢式,相當(dāng)于用空間換時(shí)間;而前面一種在需要的時(shí)候去判斷然后開(kāi)辟空間,這種方式被叫做懶漢式,就相當(dāng)于用時(shí)間來(lái)?yè)Q空間了,各有利弊。
《完》