程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 設計模式的C++實現 1.單例模式

設計模式的C++實現 1.單例模式

編輯:C++入門知識

設計模式的C++實現 1.單例模式


單例模式即實現單例類,即系統中一個類只有一個實例,而且該實例易於外界訪問。這樣方便對實例個數進行控制並節約系統資源。

而單例常用與一些非局部靜態對象,對於這些對象,程序難以控制,對於這些存在與全局,且一般持久存在的對象,有時需要按照一定約束或順序來進行初始化,而初始化這些對象如果不使用單例方法的話會極度不安全。這個時候就要使用單例模式來解決這個問題。

實現單例的方法有很多,最簡單的一個是將對象放入函數中作為其靜態成員:

class SingleTon;
SingleTon* getSingleTonInstance(){
	static SingleTon* instance = new SingleTon(); 
	return instance;
}
class SingleTon{
	friend SingleTon* getSingleTonInstance();
private:
	SingleTon(){}
};
這是我認為的最簡單的實現單例模式的方法,不足的地方在於這個獲得單例對象的函數不在類內。首先要實現單例模式,將構造函數聲明為稀有,這樣構造函數就不能被方法,也不能隨意創建單例類的對象。而這裡獲得實例為函數的靜態對象,所以其只有一個,且存在時間為創建到程序結束。

當然,也可以將函數中的靜態對象改為類中的靜態對象,而將這個全局的函數設置為類中的靜態函數,這樣就得到一個更加普遍常用的形式:

class SingleTon{
public:
	static SingleTon* getInstance(){
		static SingleTon* instance = new SingleTon();
		return instance;
	}
	~SingleTon(){}
private:
	SingleTon(){	}
	SingleTon(const SingleTon&);
	SingleTon& operator=(const SingleTon&);
};
這裡還是使用了函數中的靜態成員,使用類中的靜態成員也是可以的:

class SingleTon{
public:
	static SingleTon* getInstance(){
		if(NULL == instance)
			instance = new SingleTon();
		return instance;
	}

private:
	SingleTon(){	}
	SingleTon(const SingleTon&);
	SingleTon& operator=(const SingleTon&);
	static SingleTon* instance;
};
SingleTon* SingleTon::instance;// = new SingleTon();類內的靜態成員初始化可以調用類中的私有的構造函數。

為了安全性,這裡將復制構造函數和賦值操作符都給隱藏了,但是析構函數還是可見的,程序員還是會誤用delete來刪除這個單例實體,這樣是不安全的,可以選擇將析構函數放入私有中,隱藏析構函數,對於一些對象在最後結束時析構,則不用關心其釋放過程。

但是如果在程序執行中要調用析構函數進行實例的刪除的話,就使用一個公有的函數來封裝析構函數,且將析構函數置為私有:

class SingleTon{
public:
	static SingleTon* getInstance(){
		if(NULL == instance)
			instance = new SingleTon();
		return instance;
	}
	static void delelteInstance(){
		if(NULL != instance){
			delete instance;
			instance = NULL;
		}
	}
private:
	SingleTon(){	}
	SingleTon(const SingleTon&);
	SingleTon& operator=(const SingleTon&);
	static SingleTon* instance;
	~SingleTon();
};
SingleTon* SingleTon::instance ;

這裡就已經基本上在單線程上安全了,然後就考慮多線程,當多個線程企圖同時初始化 單例實例時,就出現了問題,要使用互斥來解決問題,這裡就使用臨界區來解決:

CRITICAL_SECTION cs;
class SingleTon{
public:
	static SingleTon* getInstance(){
		if(NULL == instance){
			EnterCriticalSection(&cs); 
			if(NULL == instance){//雙檢鎖,在進入臨界區後再檢測一次是否對象已經建立
				instance = new SingleTon();
			}
			LeaveCriticalSection(&cs);  
		}
		return instance;
	}
	static void delelteInstance(){
		if(NULL != instance){
			EnterCriticalSection(&cs);
			if(NULL != instance){
				delete instance;
				instance = NULL;
			}
			LeaveCriticalSection(&cs);  
		}
	}
private:
	SingleTon(){	}
	SingleTon(const SingleTon&);
	SingleTon& operator=(const SingleTon&);
	static SingleTon* instance;
	~SingleTon();
};
SingleTon* SingleTon::instance ;
這裡使用雙檢鎖的機制,第一次是判斷是否需要對實例進行操作,第二次是在進入臨界區即對數據加鎖後,判斷在數據已經不會再被外界干擾的情況下,第一次判斷和第二次判斷之間是否被其他線程進行了操作,這樣兩次判斷保證了實例的安全。

但是這樣還是不夠安全,因為多線程中還是會有一些特殊情況,在類中一些文件被鎖了,如文件句柄,數據庫連接等,這些隨著程序的關閉並不會立即關閉資源,必須要在程序關閉前,進行手動釋放。這裡的指不會自動關閉,是對於析構函數是私有的情況下,由於系統無法訪問私有的析構函數,對於沒有這些連接時,即類只在內存中占據了一些地址,則系統將其視為全局變量,在結束時釋放其所在內存資源,所以沒有內存洩漏。而若類中有文件句柄和數據庫連接這些東西,系統並不會幫忙關閉這些,所以必須手動的調用析構函數中對這些文件的關閉操作。

對於這樣的情況,一般會使用一種私有內嵌類Garbo,意為垃圾工人,在單例類中包含一個私有的靜態垃圾工人對象,當程序結束時,系統會調用這個對象的析構函數,而這個析構函數中對單例類對象實現析構。

CRITICAL_SECTION cs;
class SingleTon{
public:
	static SingleTon* getInstance(){
		if(NULL == instance){
			EnterCriticalSection(&cs); 
			if(NULL == instance){//雙檢鎖,在進入臨界區後再檢測一次是否對象已經建立
				instance = new SingleTon();
			}
			LeaveCriticalSection(&cs);  
		}
		return instance;
	}
	static void delelteInstance(){
		if(NULL != instance){
			EnterCriticalSection(&cs);
			if(NULL != instance){
				delete instance;
				instance = NULL;
			}
			LeaveCriticalSection(&cs);  
		}
	}	
	
private:
	SingleTon(){	}
	SingleTon(const SingleTon&);
	SingleTon& operator=(const SingleTon&);
	static SingleTon* instance;
	~SingleTon(){}//相應的關閉連接等操作
	class GarBo{
		public:
			~GarBo(){
				if(NULL != instance){
					EnterCriticalSection(&cs);
					if(NULL != instance){
						delete instance;
						instance = NULL;
					}
				LeaveCriticalSection(&cs);  
				}
			}
	};
	static GarBo gc ;
};
SingleTon* SingleTon::instance ;
SingleTon::GarBo  SingleTon::gc;//類外的初始化。
這樣就獲得一個比較完美的單例類了。


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved