設計模式回顧——單例模式(C++)
1 前言
上一篇文章描述了設計模式的概念和基本原則。本文描述結構簡單、目標明確且最常使用的設計模式——單例模式,以及C++的實現程式碼。
2 什麼是單例模式
單例模式,指的是一個類只有一個例項物件,類只提供一個訪問例項物件的全域性訪介面(方法)。
3 單例模式優缺點
優點:
- 單例模式會阻止其他物件例項化其單例物件的副本,確保唯一例項受控訪問
- 只有一個例項物件,節約系統記憶體資源
- 單例模式具有一定的伸縮性,類可以靈活更改例項化過程
- 允許可變數目的例項
- 單一例項物件,無多個例項物件共享資源的佔用問題
不足:
- 單例模式不存在抽象層,導致單列類不便於功能擴充套件
- 單例類“職責”(功能)綜合,一定程度上違背設計模式的“單一職責原則”
- 單例模式導致模組之間耦合度提高
- 濫用單例將帶來一些負面問題,如為了節省資源將資料庫連線池物件設計為的單例類,可能會導致共享連線池物件的程式過多而出現連線池溢位;如果例項化的物件長時間不被利用,系統會認為是垃圾而被回收,這將導致物件狀態的丟失
4 什麼地方使用單例模式
單例模式一般用於物件例項被其他多個模組訪問的公共場合>,典型的應用場合有:
- 需要頻繁例項化、銷燬物件的場合
- 建立物件時耗時或者消耗資源過多的物件
- 存在狀態的工具類物件
- 頻繁訪問資料庫、檔案等耗時操作的物件
上述應用場合,具體化的應用場景有:
- 日誌模組輸出
- 應用配置
- 執行緒池操作
- 工作管理員
- 資源回收器
單例模式不適用於物件可變化的場景,因為對於隨不同場景變化的物件,單例模式不能儲存彼此物件間的狀態資訊,可能引起資料錯誤。
5 單例模式實現
單例模式實現的的關鍵要點:
-
建構函式宣告為
private
,禁止類外部通過new
關鍵字例項化物件 -
拷貝建構函式同樣宣告為
private
,禁止拷貝建構函式例項化物件 -
賦值運算子函式宣告成
private
,禁止通過賦值運算子函式例項化物件 -
解構函式宣告為
private
,禁止解構函式釋放new
關鍵字建立的堆上物件 -
在類內部定義一個唯一例項物件,並宣告為
static
-
定義一個全域性訪問節點,即定義一個
static
方法返回唯一的例項物件
單例模式實現:
class Singleton
{
public:
static Singleton& GetObject()
{
static Singleton Object;
return Object;
}
void printf()
{
cout<<"to do"<<endl;
}
private:
Singleton(){} /* 禁止建構函式例項化物件 */
Singleton(Singleton const &);/* 禁止拷貝建構函式例項化物件 */
Singleton& operator=(Singleton const &);/* 禁止賦值例項化物件 */
};
int main(int argc, char **argv)
{
Singleton &a = Singleton::GetObject();
a.printf();
return 0;
}
根據類的物件例項化過程的不同,單例模式又分為“懶漢式單例”和”餓漢式單例“。
5.1 餓漢式單例
餓漢式單例指的是定義類的時候或者程式初始時執行例項化物件,使用的時候可以直接使用,無需建立。餓漢式單例,需要注意的是,採用new關鍵字生成的堆上物件,必須宣告一個public型別的方法來主動釋放物件,因為解構函式宣告為private,不會在類外被呼叫。
#include <iostream>
#include <stdlib.h>
using namespace std;
class Singleton
{
public:
static Singleton* GetObject();
void printf()
{
cout<<"to do"<<endl;
}
void DestoryObject()
{
delete m_Object;
cout<<"exe destory fun"<<endl;
}
private:
static Singleton *m_Object;
Singleton()/* 禁止建構函式例項化物件 */
{
cout<<"exe constructor fun"<<endl;
}
~Singleton()/* 禁止解構函式釋放物件 */
{
delete m_Object;
cout<<"exe destructor fun"<<endl;
}
Singleton(Singleton const &);/* 禁止拷貝建構函式例項化物件 */
Singleton& operator=(Singleton const &);/* 禁止賦值例項化物件 */
};
Singleton* Singleton::m_Object = new Singleton();
Singleton* Singleton::GetObject()
{
return m_Object;
}
不主動調銷燬函式:
int main(int argc, char **argv)
{
Singleton *a = Singleton::GetObject();
a->printf();
return 0;
}
acuity@ubuntu:/mnt/hgfs/LSW/STHB/design-mode$ ./singleton1
exe constructor fun
to do
很明顯,程式退出後並未執行解構函式,這就造成記憶體洩漏。
主動調銷燬函式:
int main(int argc, char **argv)
{
Singleton *a = Singleton::GetObject();
a->printf();
a->DestoryObject();
return 0;
}
acuity@ubuntu:/mnt/hgfs/LSW/STHB/design-mode$ ./singleton1
exe constructor fun
to do
exe destory fun
exe destructor fun
5.2 懶漢式單例
懶漢式單例指的是首次需使用類物件時才例項化物件。懶漢式單例是執行緒不安全的,因此在例項化時應該加鎖處理。
與餓漢式單例一樣,採用new關鍵字生成的堆上物件,必須宣告一個public型別的方法來主動釋放物件,因為解構函式宣告為private,不會在類外被呼叫。
懶漢式單例實現:
class Singleton
{
public:
static Singleton& GetObject();
void printf()
{
cout<<"to do"<<endl;
}
void DestoryObject()
{
delete m_Object;
cout<<"exe destory fun"<<endl;
}
private:
static Singleton *m_Object;
Singleton(){} /* 禁止建構函式例項化物件 */
~Singleton(){} /* 禁止解構函式釋放物件 */
Singleton(Singleton const &);/* 禁止拷貝建構函式例項化物件 */
Singleton& operator=(Singleton const &);/* 禁止賦值例項化物件 */
};
Singleton& Singleton::GetObject()
{
if (m_Object == NULL)
{
m_Object = new Singleton();
}
return m_Object;
}
執行緒安全的懶漢式單例實現:
class Singleton
{
public:
static Singleton& GetObject();
void printf()
{
cout<<"to do"<<endl;
}
void DestoryObject()
{
delete m_Object;
cout<<"exe destory fun"<<endl;
}
private:
static Singleton *m_Object;
static pthread_mutex_t m_mutex;
Singleton()/* 禁止建構函式例項化物件 */
{
pthread_mutex_init(&m_mutex);
}
Singleton(Singleton const &);/* 禁止拷貝建構函式例項化物件 */
~Singleton(){} /* 禁止解構函式釋放物件 */
Singleton& operator=(Singleton const &);/* 禁止賦值例項化物件 */
};
Singleton& Singleton::GetObject()
{
if (m_Object == NULL)
{
pthread_mutex_lock(&m_mutex);
m_Object = new Singleton();
pthread_mutex_lock(&m_mutex);
}
return m_Object;
}
5.3 餓漢式與懶漢式單例比較
懶漢式與餓漢式單例,各有優缺點,適用於不同場景。
-
餓漢式單例,適用於訪問量大的場景,或者多個執行緒需要訪問例項物件的場景;是一種“空間換時間”的方法
-
懶漢式單例,適用於訪問量少的場景;是一種以“時間換空間”的方法
相關文章
- 設計模式(單例模式)設計模式單例
- [設計模式] 單例模式設計模式單例
- 設計模式-單例模式設計模式單例
- 設計模式 —— 單例模式設計模式單例
- 設計模式 單例模式設計模式單例
- 設計模式——單例模式設計模式單例
- 設計模式-單例模式、多例模式設計模式單例
- 設計模式之單例設計模式設計模式單例
- 設計模式(一)_單例模式設計模式單例
- 常用設計模式-單例模式設計模式單例
- 設計模式之單例模式設計模式單例
- Java設計模式【單例模式】Java設計模式單例
- Java設計模式 | 單例模式Java設計模式單例
- 001設計模式:單例模式設計模式單例
- # Python設計模式 單例模式Python設計模式單例
- 設計模式一(單例模式)設計模式單例
- 設計模式之☞單例模式設計模式單例
- Java設計模式——單例模式Java設計模式單例
- Java設計模式–單例模式Java設計模式單例
- js設計模式--單例模式JS設計模式單例
- Java設計模式-單例模式Java設計模式單例
- 設計模式(二)——單例模式設計模式單例
- 設計模式之---單例模式設計模式單例
- Java設計模式--單例模式Java設計模式單例
- Python設計模式——單例模式Python設計模式單例
- 設計模式—singleton(單例模式)設計模式單例
- python設計模式-單例模式Python設計模式單例
- 設計模式總結 —— 單例設計模式設計模式單例
- JavaScript設計模式初探--單例設計模式JavaScript設計模式單例
- 單例設計模式單例設計模式
- Javascript設計模式之單例模式JavaScript設計模式單例
- PHP 設計模式之——單例模式PHP設計模式單例
- Javascript設計模式(三)單例模式JavaScript設計模式單例
- golang設計模式之單例模式Golang設計模式單例
- jS設計模式二:單例模式JS設計模式單例
- 前端設計模式(2)--單例模式前端設計模式單例
- JS設計模式(二)--- 單例模式JS設計模式單例
- js設計模式之單例模式JS設計模式單例