單例模式(Singleton)[分類:創建型] [目的:為程式的某個類的訪問,提供唯一物件實體]
這估計是設計模式里面最簡單的一個類了,下面我們一起看看,單例它是什么?以及如何實作一個單例
- 基本定義
- 保證一個類僅有一個實體,并提供一個訪問它的全域訪問點.
- 個人理解
- 就是一個類在整個程式里面,有且僅有一個實體,這個實體由該類自己負責創建和保存,這樣保證它不會在任何其他地方被創建,必須提供一個訪問這個實體的全域訪問介面.
- 那么有些人可能有疑問,為什么不直接用一個全域變數來控制外部對它的訪問呢?在這里如果用全域變數來控制外部的訪問它實際上還是不能避免你實體化多個物件,這樣還是達不到單例有且僅有一個實體的目的,
- 單例的特征
- 有且僅有一個實體
- 實體必須由自己創建和保存
- 要提供和給外部訪問實體的介面
- UML結構圖

- 從UML結構圖中我們可以一步步實作出以下代碼
- 簡單版本的單例模式
1 class Singleton 2 { 3 public: 4 static Singleton& GetInstance() 5 { 6 static Singleton instance; 7 return instance; 8 } 9 private: 10 Singleton(); 11 12 };
- 這里我們可以看到,這個版本建構式被私有化了,因此外部無法直接呼叫單例的構造器,而且使用靜態的區域變數做回傳,這樣不需要考慮對記憶體的管理,是不是非常方便!但是我們在對函式執行以下操作時
1 Singleton instance1=Singleton::GetInstance(); 2 Singleton instance2=Singleton::GetInstance(); 3 qDebug()<<"instance1 address:"<<&instance1; 4 qDebug()<<"instance2 address:"<<&instance2;
- 我們運行程式看結果

- 在這里instance1和instance2實體他們的地址是完全不同的.這樣的做法是不可取的,不是真正的單例,那么如何避免這樣的問題的?跟開發者說你們直接呼叫GetInstance介面就行,
- NONONO....
- 下面我們看看修改版本的單例看看
1 class SingletonModify 2 { 3 public: 4 static SingletonModify& GetInstance() 5 { 6 static SingletonModify instance; 7 return instance; 8 } 9 void sayHello(); 10 private: 11 SingletonModify(); 12 SingletonModify(SingletonModify const &);//to use this modify wrong demo 13 SingletonModify& operator =(SingletonModify const &);//to use this modify wrong demo 14 };
- 從上面我們可以看出,我們重新定義了構造器以及=操作,這里不需要實作,然后我們再用相同的方式測驗看看,這下編譯器編譯不通過了,那我們想呼叫實體的方法或者操作實體怎么辦呢?
SingletonModify::GetInstance().sayHello();//after modify code,
- 那我們只能通過上述方法訪問實體了,保證了實體的唯一性了,
- 簡單版本的單例模式技術總結
- 將構造器私有化(保證外部無法創建實體)
- 使用靜態的區域變數作為回傳(無需考慮記憶體管理的問題)
- 重新定義賦值運算子以及構造器.(避免=發生拷貝操作)
- 是不是覺得到這里單例就已經差不多啦?并不是,我們使用的是C++,但是并沒有使用到C++的一個重要特征:指標
- 首先我們針對上述簡單版本的單例模式再次進行修正,我們修改單例的函式回傳,用指標接收,看看下面代碼
1 class SingletonSimplify 2 { 3 public: 4 SingletonSimplify(); 5 static SingletonSimplify* GetInstanceptr()//simplify method 6 { 7 static SingletonSimplify instance; 8 return &instance; 9 } 10 };
- 測驗:
1 SingletonSimplify* instanceSmp1=SingletonSimplify::GetInstanceptr();//another simplify singleton method 2 SingletonSimplify* instanceSmp2=SingletonSimplify::GetInstanceptr(); 3 qDebug()<<"instanceSmp1 address:"<<instanceSmp1; 4 qDebug()<<"instanceSmp2 address:"<<instanceSmp2;

- 我們可以發現,用指標接收之后2個指標都是指向同一個地址,很好,這也是一種單例的實作方式,但是這個也屬于訪問靜態區域物件,還是沒有真正意義上使用到指標.再看看下面兩種方式
- 懶漢模式
- 先看頭檔案.h代碼
-
1 class SingletonLazyMode 2 { 3 public: 4 static SingletonLazyMode* GetInstance(); 5 static void InstanceDispose(); 6 void sayHi(); 7 private: 8 SingletonLazyMode(); 9 static SingletonLazyMode* mSingletonInstance; 10 int num=10; 11 };
-
- .cpp實作
-
1 SingletonLazyMode *SingletonLazyMode::mSingletonInstance=NULL; 2 SingletonLazyMode *SingletonLazyMode::GetInstance() 3 { 4 if(mSingletonInstance==NULL) 5 { 6 mSingletonInstance=new SingletonLazyMode(); 7 mSingletonInstance->num=20; 8 } 9 return mSingletonInstance; 10 } 11 void SingletonLazyMode::InstanceDispose() 12 { 13 if(mSingletonInstance!=NULL) 14 { 15 delete mSingletonInstance; 16 mSingletonInstance=NULL; 17 } 18 } 19 void SingletonLazyMode::sayHi() 20 { 21 qDebug()<<"hi lazy man! Number:"<<num; 22 } 23 SingletonLazyMode::SingletonLazyMode() 24 { 25 }
-
- 先看頭檔案.h代碼
- 餓漢模式
- 先看頭檔案.h代碼
-
1 class SingletonEagerMode 2 { 3 public: 4 static SingletonEagerMode* GetInstance(); 5 static void InstanceDispose(); 6 private: 7 SingletonEagerMode(); 8 static SingletonEagerMode* mEagerInstance; 9 };
-
- .cpp實作
-
1 SingletonEagerMode *SingletonEagerMode::mEagerInstance=new SingletonEagerMode(); 2 SingletonEagerMode *SingletonEagerMode::GetInstance() 3 { 4 return mEagerInstance; 5 } 6 void SingletonEagerMode::InstanceDispose() 7 { 8 if(mEagerInstance!=NULL) 9 { 10 delete mEagerInstance; 11 mEagerInstance=NULL; 12 } 13 } 14 SingletonEagerMode::SingletonEagerMode() 15 { 16 17 }
-
- 先看頭檔案.h代碼
- 測驗代碼
-
1 SingletonLazyMode* lazyinstance1=SingletonLazyMode::GetInstance();//lazy mode 懶漢模式 2 SingletonLazyMode* lazyinstance2=SingletonLazyMode::GetInstance(); 3 lazyinstance1->sayHi(); 4 lazyinstance2->sayHi(); 5 qDebug()<<"lazyinstance1 address:"<<lazyinstance1; 6 qDebug()<<"lazyinstance2 address:"<<lazyinstance2; 7 SingletonLazyMode::InstanceDispose(); 8 qDebug()<<"lazyinstance1 address:"<<lazyinstance1; 9 qDebug()<<"lazyinstance2 address:"<<lazyinstance2; 10 lazyinstance1->sayHi(); 11 lazyinstance2->sayHi(); 12 SingletonEagerMode* eagerinstance1=SingletonEagerMode::GetInstance();//eager mode 餓漢模式 13 SingletonEagerMode* eagerinstance2=SingletonEagerMode::GetInstance(); 14 qDebug()<<"eagerinstance1 address:"<<eagerinstance1; 15 qDebug()<<"eagerinstance2 address:"<<eagerinstance2;
-
- 運行效果:

- 這里我們可以看到不管是懶漢還是餓漢模式兩個實體的地址均是相同的,說明我們的單例是OK的
- 技術總結:
- 下面對懶漢模式和餓漢模式經行對比分析其異同點:
- 相同點:
- 懶漢/餓漢模式實作結構基本類似
- 不同點:
- 懶漢模式初始化物件是在程式呼叫的時候,非執行緒安全,由于最終實作要加Qmutex進行枷鎖處理,執行效率會相對而言要低
- 餓漢模式是程式啟動的時候就已經創建好了,浪費記憶體,但屬于執行緒安全,執行效率相對懶漢而言要高
- 相同點:
- 問題點:
- 細心的同學可能發現了,在上面測驗程序中我呼叫了自己定義的Dispose介面,但是還是能再次呼叫lazyinstance1,lazyinstance2實體中的函式和變數???這是在MinGW編譯器下執行的結果,當我將編譯器換成MSVC時,顯示記憶體已經被釋放掉了
- 下圖MSVC下的執行結果
參考了一篇博客也沒看出什么問題:https://www.cnblogs.com/chengjundu/p/11283123.html
-
如果有朋友知道望不吝賜教!!!!感謝,
-
最后看看我們執行緒安全的懶漢實作方式
-
.h檔案
1 class SingletonThreadSafety 2 { 3 public: 4 static SingletonThreadSafety* GetInstance(); 5 static void InstanceDispose(); 6 private: 7 SingletonThreadSafety(); 8 static SingletonThreadSafety* mSaftyInstance; 9 static QMutex mMutex; 10 };
- .cpp代碼
-
1 SingletonThreadSafety *SingletonThreadSafety::mSaftyInstance=NULL; 2 QMutex SingletonThreadSafety::mMutex; 3 SingletonThreadSafety *SingletonThreadSafety::GetInstance() 4 { 5 if(mSaftyInstance==NULL) 6 { 7 QMutexLocker locker(&mMutex); 8 if(mSaftyInstance==NULL) 9 { 10 mSaftyInstance=new SingletonThreadSafety(); 11 } 12 } 13 return mSaftyInstance; 14 } 15 void SingletonThreadSafety::InstanceDispose() 16 { 17 if(mSaftyInstance!=NULL) 18 { 19 delete mSaftyInstance; 20 mSaftyInstance=NULL; 21 } 22 } 23 SingletonThreadSafety::SingletonThreadSafety() 24 { 25 26 }
這就完美解決掉了執行緒安全問題,但是在獲取實體物件的時候需要對Qmutex進行判斷,這會損失一點點性能,
- 以上就是對單例模式的完整概述
- 下面進行全面的技術總結:
- 在寫單例模式的時候我們要考慮到以下幾個方面:
- 要封閉默認的建構式,以防止多地方創建物件
- 類提供一個靜態的物件,用來保存該實體
- 提供一個公共的訪問實體的介面GetInstance
- 考慮執行緒安全問題
以上單例所有內容,如有錯誤請指出!!!
參考<<大話設計模式>>一書
附源代碼:
https://gitee.com/xiaochunlu/designer-pattern/tree/master/Singleton_Pattern
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/107749.html
標籤:其他
下一篇:iOS_iOS14.0適配
