1. 程式人生 > >設計模式——單例模式(C++實現)

設計模式——單例模式(C++實現)

一、單例模式定義:

保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點,該例項被所有程式模組共享。

二、應用場景:

比如在某個伺服器程式中,該伺服器的配置資訊存放在一個檔案中,這些配置資料由一個單例物件統一讀取,然後服務程序中的其他物件再通過這個單例物件獲取這些配置資訊。這種方式簡化了在複雜環境下的配置管理。其他還有如系統的日誌輸出、MODEM的聯接需要一條且只需要一條電話線,作業系統只能有一個視窗管理器,一臺PC連一個鍵盤等等。

三、方式:

根據單例物件建立時間,可分為兩種模式:餓漢模式 + 懶漢模式。

  • 懶漢模式:指全域性的單例例項在第一次被使用時構建。

  • 餓漢模式:指全域性的單例例項在類裝載時構建。

四、程式碼實現

1.懶漢模式

class Singleton {
private:
	Singleton() {}  //建構函式是私有的
	static Singleton *instance;
public:
	static Singleton * GetInstance() {
		if(instance == nullptr)  //判斷是否第一次呼叫
			instance = new Singleton();
		return instance;
	}
};
Singleton * Singleton::instance = nullptr;

單例類Singleton有以下特徵:
它有一個指向唯一例項的靜態指標instance,並且是私有的;
它有一個公有的函式,可以獲取這個唯一的例項,並且在需要的時候建立該例項;
它的建構函式是私有的,這樣就不能從別處建立該類的例項。

2.懶漢模式+物件釋放

程式在結束的時候,系統會自動析構所有的全域性變數。事實上,系統也會析構所有的類的靜態成員變數,就像這些靜態成員也是全域性變數一樣。利用這個特徵,我們可以在單例類中定義一個這樣的靜態成員變數,而它的唯一工作就是在解構函式中刪除單例類的例項。如下面的程式碼中的Garbo類(Garbo意為垃圾工人):

class Singleton {
private:
	Singleton() {}
	static Singleton *m_pInstance;
	class Garbo { //它的唯一工作就是在解構函式中刪除Singleton的例項
	public:
		~Garbo() {
			if(Singleton::m_pInstance)
				delete Singleton::m_pInstance;
		}
	};
	static Garbo garbo;  //定義一個靜態成員變數,程式結束時,系統會自動呼叫它的解構函式
public:
	static Singleton * GetInstance() {
		if(m_pInstance == nullptr)  //判斷是否第一次呼叫
			m_pInstance = new Singleton();
		return m_pInstance;
	}
};
Singleton * Singleton::m_pInstance = nullptr;

類Garbo被定義為Singleton的私有內嵌類,以防該類被在其他地方濫用。
程式執行結束時,系統會呼叫Singleton的靜態成員garbo的解構函式,該解構函式會刪除單例的唯一例項。
使用這種方法釋放單例物件有以下特徵:
在單例類內部定義專有的巢狀類;
在單例類內定義私有的專門用於釋放的靜態成員;
利用程式在結束時析構全域性變數的特性,選擇最終的釋放時機;
使用單例的程式碼不需要任何操作,不必關心物件的釋放。

3.餓漢式

使用區域性靜態變數,非常強大的方法,完全實現了單例的特性,而且程式碼量更少,也不用擔心單例銷燬的問題。

class Singleton {
private:
	Singleton() {} //建構函式是私有的
public:
	static Singleton & getInstance() {
		static Singleton instance = new Singleton(); //區域性靜態變數
		return instance;
	}
};

但是當以如下方法使用單例時問題來了:
Singleton singleton = Singleton::GetInstance();
這麼做就出現了一個類拷貝的問題,這就違背了單例的特性。產生這個問題原因在於:編譯器會為類生成一個預設的建構函式,來支援類的拷貝。
我們要禁止類拷貝和類賦值,禁止程式設計師用這種方式來使用單例:

class Singleton {
private:
	Singleton() {} //建構函式是私有的
	Singleton(const Singleton &);
	Singleton & operator = (const Singleton &);
public:
	static Singleton & GetInstance() {
		static Singleton instance = new Singleton(); //區域性靜態變數
		return instance;
	}
};

4.執行緒安全——雙重檢測鎖定(Double Checked Locking)

class Singleton {
private:
	Singleton() {}  //建構函式是私有的
	static Singleton *instance;
public:
	static Singleton * GetInstance() {
            if(instance == nullptr) { //判斷是否第一次呼叫
                lock();
                if(instance == nullptr)
                    instance = new Singleton();
                unlock();
            }
	    return instance;
	}
};
Singleton * Singleton::instance = nullptr;
 

DCL用於在多執行緒環境下保證單一建立Singleton物件。第一次check不用加鎖,但是第二次check和建立物件必須加鎖。由於編譯器可能會優化程式碼,亂序執行,可能導致DCL失效。例如:

m_Instance = new Singleton(); 這個語句會分成三步完成:

(1)分配記憶體,

(2)在已經分配的記憶體上呼叫建構函式建立物件,

(3)將物件賦值給指標m_Instance .

但是這個順序很可能會被改變為1,3,2。如果A執行緒在1,3執行完後,B執行緒執行第一個條件判斷if(m_Instance ==0),此時鎖不能起到保護作用。B執行緒會認為m_Instance 已經指向有效物件,可以去使用了。嘿嘿,災難發生。

volatile對於執行順序也沒有幫助,解決不了DCL的問題。

5.執行緒安全——pthread_once

template<typename T>
class Singleton : boost::noncopyable {
public:
    static T& getInstance() {
        pthread_once(&ponce_, &Singleton::init);
        return *value_;
    }
private:
    Singleton();
    ~Singleton();
    static void init() {
        value_ = new T();
    }
private:
    static pthread_once_t ponce_;
    static T*             value_;
};

template<typename T>
pthread_once_t Singleton<T>::ponce_ = PTHREAD_ONCE_INIT;

template<typename T>
T * Singleton<T>::value_ = nullptr;

參考《Linux多執行緒服務端程式設計》陳碩 2.5節。

使用pthread_once_t來保證執行緒安全。執行緒安全性由Pthreads庫保證。

沒有考慮物件的銷燬,在長時間執行的伺服器程式裡,這不是一個問題,反正程序也不打算正常退出。在短期執行的程式中,程式退出時自然就釋放所有資源了。

相關推薦

設計模式——模式C++實現

一、單例模式定義: 保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點,該例項被所有程式模組共享。 二、應用場景: 比如在某個伺服器程式中,該伺服器的配置資訊存放在一個檔案中,這些配置資料由一個單例物件統一讀取,然後服務程序中的其他物件再通過這個單例物件獲取這些配置

Head First 設計模式C++實現模式:Singleton

單例模式:確保一個類只有一個例項,並提供一個全域性訪問點 1 .經典單例模式實現   我們都很清楚一個簡單的單例模式該怎樣去實現:建構函式宣告為private或protect防止被外部函式例項化,內部儲存一個private static的類指標儲存唯一的例項,例項的動

設計模式-模式-C++實現

單例模式:保證一個類提供且僅提供一例項,並提供一個訪問它的全域性訪問點。 場景: 1.當類只能有一個例項並且客戶可以從一個公共的介面訪問到例項; 2.當這個唯一的例項應該是通過子類化可擴充套件的,並且客戶應該無需更改程式碼就能使用一個擴充套件的例項。 #include <s

設計模式-模式餓漢式及懶漢式的Java實現

單例模式 單例模式在程式設計中使用的頻率非常之高,其設計的目的是為了在程式中提供唯一一個物件(保證只被構造一次),例如寫入日誌的log物件,windows的工作管理員實現(只能開啟一個)。這裡主要介紹單例模式使用Java的實現(包括餓漢式及懶漢式)。 實現

設計模式——模式C++實現

引出問題: 設計一個類,我們只能生成該類的一個物件例項。 不好的解法:只適用於單執行緒環境 因為該類只能生成一個物件例項,那麼該類的建構函式必須是私有的,從而避免他人建立例項。在需要的時候建立該類的一個物件。 下面是程式實現: /****************

設計模式——抽象工廠模式C++實現

concrete out png return style bsp ctp img using 1 #include <iostream> 2 #include <string> 3 4 usin

設計模式——觀察者模式C++實現

ace mes des ret rtu cto pattern virt date 1 #include <iostream> 2 #include <vector> 3 #include <algorithm>

[轉]設計模式--模式懶漢式和餓漢式

打印 是否 調用構造 餓漢 一段 tools 會有 輸出結果 java 單例模式是設計模式中比較簡單的一種。適合於一個類只有一個實例的情況,比如窗口管理器,打印緩沖池和文件系統, 它們都是原型的例子。典型的情況是,那些對象的類型被遍及一個軟件系統的不同對象訪問,因此需要一個

設計模式——命令模式C++實現

clear cto ive pre urn bak std oot style 1 [root@ ~/learn_code/design_pattern/19_order]$ cat order.cpp 2 #include <

設計模式——職責鏈模式C++實現

delet hand jin void ng- nbsp request req oot   1 #include <iostream> 2 #include <string> 3 4 using namesp

設計模式——中介者模式/調停者模式C++實現

con 分享 else .cn sign name 得到 ted esp 1 #include <iostream> 2 #include <string> 3 4 using namespace std;

設計模式——模式(C++)

clu win 安全 iostream public size turn instance stat 一: 餓漢式單例: 靜態區初始化instance,然後通過getInstance返回。這種方式沒有多線程的問題,是一種以空間換時間的方式,不管程序用不用,都會構造唯一的

C#設計模式--模式

資源 let readonly eat 私有靜態變量 sta 技術分享 span ret 目的:避免對象的重復創建 單線程具體的實現代碼 /// <summary> /// 私有化構造函數 /// </summary>

C++設計模式-模式

con git god www light nullptr return post gpo 版權聲明:若無來源註明,Techie亮博客文章均為原創。 轉載請以鏈接形式標明本文標題和地址: 本文標題:C++設計模式-單例模式 本文地址:http://techieli

C#設計模式——模式

code 關閉 object 需要 rm2 學習 single C# oid 一、單例模式定義: 確保一個類只有一個實例,並提供一個訪問它的全局訪問點。 二、背景: 當我們的系統中某個對象只需要一個實例的情況,例如:操作系統中只能有一個任務管理器,操作文件時,同一時間內只允

php實現設計模式————模式

  php實現設計模式————單例模式    什麼是單例模式    為什麼要使用單例模式    php中有哪些方式實現新建一個物件例項    如何阻止這種例項化實現理想的單例模式    程式碼實現    什麼是單例模式    為什麼要使用單例模式    php中有哪些方式實現新建一個物件例項    1. ne

C#設計模式 —— 模式

  嗯,這是本人的第一篇隨筆,就從最簡單的單例模式開始,一步一步地記錄自己的成長。   單例模式是最常見的設計模式之一,在專案程式碼中幾乎隨處可見。這個設計模式的目的就是為了保證例項只能存在一個。單例模式往下還能再細分為懶漢模式和餓漢模式。下面逐個來看。 1.餓漢模式   餓漢模式的做法是在類載入的時候

設計模式模式關鍵詞:設計模式/模式/單件模式

設計模式 單例模式 定義 單例模式:允許一些物件中只存在 1 個例項。 類裝飾器版本 1:使用 1 個全域性字典,儲存所有類的例項。 instances = {} def getInstances(aClass, *args): if aClass not in i

設計模式|模式2 模式下反序列化和反射帶來的安全問題

接上篇 單例模式(1) 序列化破壞單例模式 餓漢式的單例類 public class HungarySingleton { private final static HungarySingleton hungarySingleton = new Hu

Java設計模式-模式模式

餓漢式單例類 餓漢式單例類在載入時單例物件已經被建立。程式碼如下: /** * 懶漢式單例 * @Author: Rick * @Date: 2018/10/31 17:44 */ public class EagerSingleton { pr