溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++中的單例模式——2

發(fā)布時間:2020-06-17 16:36:20 來源:網(wǎng)絡 閱讀:218 作者:給我個bit位 欄目:編程語言

    有時候?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();


    上面一次性就將對象空間給開辟出來每次不用判斷就直接返回,這種方式被稱為餓漢式,相當于用空間換時間;而前面一種在需要的時候去判斷然后開辟空間,這種方式被叫做懶漢式,就相當于用時間來換空間了,各有利弊。


《完》

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI