您好,登錄后才能下訂單哦!
有時候?qū)崿F(xiàn)了一個類,但只需要創(chuàng)建出一個實例化的對象就能完成需求,如果有太多的對象不僅浪費內(nèi)存空間也會使得代碼數(shù)據(jù)不那么好維護,因此會需要設計出一個只能生成一個實例的類;
首先,要使得這個類只能實例化出一個對象,那么它的構造函數(shù)肯定不能夠被外部隨意調(diào)用,因此應該將類的構造函數(shù)的訪問限定符設定為私有的,但是這樣的話,如何獲得唯一一個的實例化對象呢?可以設計一個成員函數(shù),這個成員函數(shù)只能分配出唯一一個該類對象的內(nèi)存空間,并且返回指向這塊空間的指針給申請的對象,因此,可以將類設計為如下:
#include <iostream> using 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;
上面的栗子中將構造函數(shù)、拷貝構造和賦值運算符的重載函數(shù)的訪問限定符置為私有的,外界不能通過這些函數(shù)實例化出對象;然后設計了一個靜態(tài)成員函數(shù),這里值得一提的是,靜態(tài)的成員函數(shù)沒有隱含的this指針,因此外界可以通過類名加域的訪問符號::來調(diào)用靜態(tài)的成員函數(shù);
類的成員變量只定義了一個指向類對象的一個_instance指針,靜態(tài)的成員函數(shù)可以在類外部初始化,先將其初始化為NULL,當?shù)谝淮握{(diào)用這個GetInstance函數(shù)的時候,_instance為NULL,因此為其分配一個類對象的空間并將其返回;但是當?shù)诙握{(diào)用這個函數(shù)的時候,_instance已經(jīng)有了值并不會再分配空間而是返回已經(jīng)存在的實例化出的對象,因此無論怎么調(diào)用GetInstance函數(shù)都只會返回同一塊地址空;
但是上面的函數(shù)在單線程環(huán)境下運行是沒有問題的,當有多個線程并發(fā)訪問這個函數(shù)的時候,如果兩個或多個線程同時拿到了初始化為NULL的_instance的值要進行判斷的時候,有可能都會判斷成功,也就是會new出來兩塊不同的地址空間,這樣就不符合設計的初衷了,因此在多線程運行環(huán)境下,GetInstance函數(shù)中的代碼就會成為臨界區(qū),也就是要有互斥的關系,可以為其加上mutex互斥鎖:
static Singleton*& GetInstance() { pthread_mutex_lock(&lock); if(_instance == NULL) { _instance = new Singleton(); } pthread_mutex_unlock(&lock); return _instance; }
可是上面的代碼還是存在一些效率的問題,如果一個線程最開始獲得了鎖并且成功new出了空間,那么之后的線程每一次進到函數(shù)GetInstance里面都要爭奪一下鎖資源并且再依次判斷,所以,可以在加鎖之前就先進行一次判斷,如果_instance不為空,后面就沒有必要競爭鎖資源再進行判斷了,所以代碼可以優(yōu)化為如下:
static Singleton*& GetInstance() { if(_instance == NULL) { pthread_mutex_lock(&lock); if(_instance == NULL) { _instance = new Singleton(); } pthread_mutex_unlock(&lock); } return _instance; }
其實除了上面所給出的解法,還有另外一種簡單粗暴的設計方式,那就是直接在給靜態(tài)成員變量_instance初始化的時候就初始化為new出來的一個類的實例化對象,之后每一次調(diào)用GetInstance函數(shù)獲取_instance的值的時候就直接返回:
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();
上面一次性就將對象空間給開辟出來每次不用判斷就直接返回,這種方式被稱為餓漢式,相當于用空間換時間;而前面一種在需要的時候去判斷然后開辟空間,這種方式被叫做懶漢式,就相當于用時間來換空間了,各有利弊。
《完》
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。