亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

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

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

軟件開發常用設計模式—單例模式總結(c++版)

發布時間:2020-07-09 20:51:15 來源:網絡 閱讀:243 作者:張濤澤 欄目:網絡安全

單例模式:就是只有一個實例。

singleton pattern單例模式:確保某一個類在程序運行中只能生成一個實例,并提供一個訪問它的全局訪問點。這個類稱為單例類。如一個工程中,數據庫訪問對象只有一個,電腦的鼠標只能連接一個,操作系統只能有一個窗口管理器等,這時可以考慮使用單例模式。

眾所周知,c++中,類對象被創建時,編譯系統為對象分配內存空間,并自動調用構造函數,由構造函數完成成員的初始化工作,也就是說使用構造函數來初始化對象。

1、那么我們需要把構造函數設置為私有的 private,這樣可以禁止別人使用構造函數創建其他的實例。

2、又單例類要一直向系統提供這個實例,那么,需要聲明它為靜態的實例成員,在需要的時候,才創建該實例。

3、且應該把這個靜態成員設置為 null,在一個public 的方法里去判斷,只有在靜態實例成員為 null,也就是沒有被初始化的時候,才去初始化它,且只被初始化一次。

通常我們可以讓一個全局變量使得一個對象被訪問,但它不能阻止你實例化多個對象。如果采用全局或者靜態變量的方式,會影響封裝性,難以保證別的代碼不會對全局變量造成影響。

一個最好的辦法是,讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例可以被創建,并且它可以提供一個訪問該實例的方法,單例模式比全局對象好還包括,單例類可以繼承。

單例模式又分為兩種基本的情形:餓漢式和懶漢式

直接在靜態區初始化 instance,然后通過 get 方法返回,這樣這個類每次直接先生成一個對象,好像好久沒吃飯的餓漢子,急著吃飯一樣,急切的 new 對象,這叫做餓漢式單例類。或者是在 get 方法中才 new instance,然后返回這個對象,和懶漢字一樣,不主動做事,需要調用 get 方法的時候,才 new 對象,這就叫做懶漢式單例類。

 

如下是懶漢式單例類

軟件開發常用設計模式—單例模式總結(c++版)

 1 //單例模式示例 2 class Singleton 3 { 4 public: 5     static Singleton * getInstance() 6     { 7         if (instance == NULL) { 8             instance = new Singleton(); 9         }10         11         return instance;12     }13     14 private:15     //私有的構造函數,防止外人私自調用16     Singleton()17     {18         cout << "實例化了" << count << "個對象!" << endl;19         count++;20     }21     //聲明一個靜態實例,靜態函數只能使用靜態的數據成員。整個類中靜態成員只有一個實例,通常在實現源文件中被初始化。22     static Singleton *instance;23     //記錄實例化的對象24     int count = 1;25 };26 27 Singleton * Singleton::instance = NULL;28 29 int main(void)30 {31     Singleton::getInstance();32     Singleton::getInstance();33     Singleton::getInstance();34     Singleton::getInstance();35     36     return 0;37 }

軟件開發常用設計模式—單例模式總結(c++版)

實例化了1個對象!

Program ended with exit code: 0

小結:

懶漢式單例模式是用時間換取控件,餓漢式單例模式,是用空間換取時間。

 

繼續分析,考慮多線程下的懶漢式單例模式

上述代碼在單線程的情況下,運行正常,但是遇到了多線程就出問題,假設有兩個線程同時運行了這個單例類,同時運行到了判斷 if 語句,并且當時,instance 實例確實沒有被初始化呢,那么兩個線程都會去運行并創建實例,此時就不滿足單例類的要求了。那么我們需要寫上線程同步的功能。

軟件開發常用設計模式—單例模式總結(c++版)

 1 //考慮到多線程情形下的單例模式 2 class Singleton 3 { 4 public: 5     //get 方法 6     static Singleton * getInstance(){ 7         //聯系互斥信號量機制,給代碼加鎖 8         lock(); 9         //判斷 null10         if (NULL == instance) {11             //判斷類沒有生成對象,才實例化對象,否則不再實例化12             instance = new Singleton();13         }14         //使用完畢,解鎖15         unlock();16         //返回一個實例化的對象17         return instance;18     }19 private:20     //聲明對象計數器21     int count = 0;22     //聲明一個靜態的實例23     static Singleton *instance;24     //私有構造函數25     Singleton(){26         count++;27         cout << "實例化了" << count << "個對象!" << endl;28     }29 };30 //初始化 instance31 Singleton * Singleton::instance = NULL;

軟件開發常用設計模式—單例模式總結(c++版)

此時,還是有 ab 兩個線程來運行這個單例類,由于在同一時刻,只有一個線程能拿到同步鎖(互斥信號量機制),a 拿到了同步鎖,b 只能等待,如果 a發現實例還沒創建,a 就會創建一個實例,創建完畢,a 釋放同步鎖,然后 b 才能拿到同步鎖,繼續運行接下來的代碼,b 發現 a 線程運行的時候,已經生成了一個實例,b 線程就不會重復創建實例了,這樣就保證了我們在多線程環境中只能得到一個實例。

 

繼續分析多線程下的懶漢式單例模式

代碼中,每次 get 方法中,得到 instance,都要判斷是否為空,且判斷是否為空之前,都要先加同步鎖,如果線程很多的時候,就要先等待加了同步鎖的線程運行完畢,才能繼續判斷余下的線程,這樣就會造成大量線程的阻塞,且加鎖是個非常消耗時間的過程,應該盡量避免(除非很有必要的時候)。可行的辦法是,雙重判斷方法。

因為,只是在實例還沒有創建的時候,需要加鎖判斷,保證每次只有一個線程創建實例,而當實例已經創建之后,其實就不需要加鎖操作了。

 

雙重判斷的線程安全的懶漢式單例模式 

軟件開發常用設計模式—單例模式總結(c++版)

 1 class Singleton 2 { 3 public: 4     //get 方法 5     static Singleton * getInstance(){ 6         //先判斷一次 null,只有 null 的時候需要加鎖,其他的時候,其實不需要加鎖 7         if (NULL == instance) { 8             //聯系互斥信號量機制,給代碼加鎖 9             lock();10             //然后再次判斷 null11             if (NULL == instance) {12                 //判斷類沒有生成對象,才實例化對象,否則不再實例化13                 instance = new Singleton();14             }15             //使用完畢,解鎖16             unlock();17         }18                 //返回一個實例化的對象19         return instance;20     }21 private:22     //聲明對象計數器23     int count = 0;24     //聲明一個靜態的實例25     static Singleton *instance;26     //私有構造函數27     Singleton(){28         count++;29         cout << "實例化了" << count << "個對象!" << endl;30     }31 };32 //初始化 instance33 Singleton * Singleton::instance = NULL;

軟件開發常用設計模式—單例模式總結(c++版)

這樣的雙重檢測機制,提高了單例模式在多線程下的效率,因為這樣的代碼,只需要在第一次創建實例的時候,需要加鎖,其他的時候,新航道雅思培訓線程無需排隊等待加鎖之后,再去判斷了,比較高效。

 

再看餓漢式的單例模式,之前看了懶漢式的單例類,是線程不安全的,通過加鎖(雙重鎖),實現線程安全

回憶餓漢式單例類:直接在靜態區初始化 instance,然后通過 get 方法返回,這樣這個類每次直接先生成一個對象,好像好久沒吃飯的餓漢子,急著吃飯一樣,急切的 new 對象,這叫做餓漢式單例類。

軟件開發常用設計模式—單例模式總結(c++版)

 1 class Singleton 2 { 3 public: 4     //get 方法 5     static Singleton * getInstance(){ 6         //返回一個實例化的對象 7         return instance; 8     } 9 private:10     //聲明一個靜態的實例11     static Singleton *instance;12     //私有構造函數13     Singleton(){14     15     }16 };17 //每次先直接實例化instance,get 方法直接返回這個實例18 Singleton * Singleton::instance = new Singleton();

軟件開發常用設計模式—單例模式總結(c++版)

注意:靜態初始化實例可以保證線程安全,因為靜態實例初始化在程序開始時進入主函數之前,就由主線程以單線程方式完成了初始化!餓漢式的單例類,也就是靜態初始化實例保證其線程安全性,故在性能需求較高時,應使用這種模式,避免頻繁的鎖爭奪。

 

繼續看單例模式

上面的單例模式沒有 destory() 方法,也就是說,貌似上面的單例類沒有主動析構這個唯一實例!然而這就導致了一個問題,在程序結束之后,該單例對象沒有delete,導致內存泄露!下面是一些大神的方法:一個妥善的方法是讓這個類自己知道在合適的時候把自己刪除,或者說把刪除自己的操作掛在操作系統中的某個合適的點上,使其在恰當的時候被自動執行。

我們知道,程序在結束的時候,系統會自動析構所有的全局變量。事實上,系統也會析構所有的類的靜態成員變量,就像這些靜態成員也是全局變量一樣。如果在類的析構行為中有必須的操作,比如關閉文件,釋放外部資源,那么上面的代碼無法實現這個要求。我們需要一種方法,正常的刪除該實例。利用這些特征,我們可以在單例類中定義一個這樣的靜態成員變量,而它的唯一工作就是在析構函數中刪除單例類的實例。如下面的代碼中的Garbage類:

軟件開發常用設計模式—單例模式總結(c++版)

 1 class Singleton 2 { 3 public: 4     //get 方法 5     static Singleton * getInstance(){ 6         //判斷單例否 7         if (NULL == instance) { 8             instance = new Singleton(); 9         }10         //返回一個實例化的對象11         return instance;12     }13     //c++ 嵌套的內部類,作用是刪除單例類對象,Garbage被定義為Singleton的內嵌類,以防該類被在其他地方濫用。14     class Garbage15     {16     public:17         ~Garbage(){18             if (Singleton::instance != NULL) {19                 cout << "單例類的唯一實例被析構了" << endl;20                 delete Singleton::instance;21             }22         }23     };24     25 private:26     //單例類中聲明一個觸發垃圾回收類的靜態成員變量,它的唯一工作就是在析構函數中刪除單例類的實例,利用程序在結束時析構全局變量的特性,選擇最終的釋放時機;27     static Garbage garbage;28     //聲明一個靜態的實例29     static Singleton *instance;30     //單例類的私有構造函數31     Singleton(){32         cout << "調用了單例類的構造函數" << endl;33     }34     //單例類的私有析構函數35     ~Singleton(){36         cout << "調用了單例類的析構函數" << endl;37     }38 };39 //初始化內部的靜態變量,目睹是啟動刪除的析構函數,如果不初始化,就不會被析構40 //內部類可以訪問外部類的私有成員,外部類不能訪問內部類的私有成員!41 Singleton::Garbage Singleton::garbage;42 //初始化instance為 null43 Singleton * Singleton::instance = NULL;44 45 int main(void)46 {47     Singleton *a = Singleton::getInstance();48     Singleton *b = Singleton::getInstance();49     Singleton *c = Singleton::getInstance();50     51     if (a == b) {52         cout << "a = b" << endl;53     }54     55     return 0;56 }

軟件開發常用設計模式—單例模式總結(c++版)

調用了單例類的構造函數

a = b

單例類的唯一實例被析構了

調用了單例類的析構函數

Program ended with exit code: 0

 

類Garbage被定義為Singleton的內嵌類,以防該類在其他地方濫用,程序運行結束時,系統會調用Singleton的靜態成員garbage的析構函數,該析構函數會刪除單例的唯一實例,使用這種方法釋放單例對象有以下特征:

1、在單例類內部定義專有的嵌套類;

2、在單例類內定義私有的專門用于釋放的靜態成員;

3、利用程序在結束時析構全局變量的特性,選擇最終的釋放時機;

4、使用單例的代碼不需要任何操作,不必關心對象的釋放。

 

其實,繼續想單例類的實現,有的人會這樣做:

在程序結束時調一個專門的方法,這個方法里判斷實例對象是否為 null,如果不為 null,就對返回的指針掉用delete操作。這樣做可以實現刪除單例的功能,但不僅很丑陋,而且容易出錯。因為這樣的附加代碼很容易被忘記,而且也很難保證在delete之后,沒有代碼再調用GetInstance函數。不推薦直接的刪除方法。

 

繼續查看單例模式:單例模式在實際開發過程中是很有用的,單例模式的特征總結:

1、一個類只有一個實例

2、提供一個全局訪問點

3、禁止拷貝

逐個分析:

1、實現只有一個實例,需要做的事情:將構造函數聲明為私有

2、提供一個全局訪問點,需要做的事情:類中創建靜態成員和靜態成員方法

3、禁止拷貝:把拷貝構造函數聲明為私有,并且不提供實現,將賦值運算符聲明為私有,防止對象的賦值

完整的單例類實現代碼如下:

軟件開發常用設計模式—單例模式總結(c++版)

class Singleton
{public:    //get 方法
    static Singleton * getInstance(){        if (NULL == instance) {            lock();            //判斷單例否
            if (NULL == instance) {
                instance = new Singleton();
            }
            unlock();
        }        //返回一個實例化的對象
        return instance;
    }    //c++ 嵌套的內部類,作用是刪除單例類對象,Garbage被定義為Singleton的私有內嵌類,以防該類被在其他地方濫用。
    class Garbage
    {    public:        ~Garbage(){            if (Singleton::instance != NULL) {
                cout << "單例類的唯一實例被析構了" << endl;
                delete Singleton::instance;
            }
        }
    };    
private:    //單例類中定義一個這樣的靜態成員變量,而它的唯一工作就是在析構函數中刪除單例類的實例,利用程序在結束時析構全局變量的特性,選擇最終的釋放時機;
    static Garbage garbage;    //聲明一個靜態的實例
    static Singleton *instance;    //單例類的私有構造函數    Singleton(){
        cout << "調用了單例類的構造函數" << endl;
    }    //單例類的私有析構函數
    ~Singleton(){
        cout << "調用了單例類的析構函數" << endl;
    }    //把拷貝構造函數聲明為私有,就可以禁止外人拷貝對象,也不用實現它,聲明私有即可
    Singleton(const Singleton &copy);    //把賦值運算符重載為私有的,防止對象之間的賦值操作
    Singleton & operator=(const Singleton &other);
};//初始化內部似有淚的靜態變量,目睹是啟動刪除的析構函數,如果不初始化,就不會被析構//內部類可以訪問外部類的私有成員,外部類不能訪問內部類的私有成員!Singleton::Garbage Singleton::garbage;//初始化instance為 nullSingleton * Singleton::instance = NULL;int main(void)
{
    Singleton *a = Singleton::getInstance();
    Singleton *b = Singleton::getInstance();
    Singleton *c = Singleton::getInstance();    
    if (a == b) {
        cout << "a = b" << endl;
    }    
    return 0;
}

軟件開發常用設計模式—單例模式總結(c++版)

單例類de測試,兩個方法:

1、實例化多個對象,看調用了幾次構造函數,如果只調用一次,說明只創建一個實例

2、單步跟蹤,查看對象的地址,是否一樣,一樣則為一個對象


向AI問一下細節

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

AI

色达县| 晋城| 佛教| 丰原市| 新安县| 左贡县| 南华县| 嘉定区| 万山特区| 乐平市| 马公市| 永兴县| 称多县| 宁夏| 句容市| 乌拉特前旗| 聊城市| 松滋市| 儋州市| 广元市| 连州市| 苍梧县| 迭部县| 神木县| 文昌市| 崇明县| 托里县| 临洮县| 当阳市| 莎车县| 东兰县| 肇源县| 察隅县| 上犹县| 中西区| 天门市| 疏勒县| 临邑县| 永州市| 特克斯县| 营口市|