單例模式簡介

什麼是設計模式設計模式

設計模式表明了最佳實踐,是軟件開發過程當中面臨通常問題的解決方案。安全

設計模式是一套被反覆使用、通過分類、代碼設計總結的經驗。併發

單例模式ide

單例模式也叫單件模式。Singleton是一個很是經常使用的設計模式,幾乎全部稍微大一些的程序都會使用到它,因此構建一個線程安全而且高效的Singleton很重要。函數

1. 單例類保證全局只有一個惟一實例對象。高併發

2. 單例類提供獲取這個惟一實例的接口。優化

怎樣設計一個單例模式spa


實現一(不考慮線程安全)線程

class Singleton
{
public:
// 獲取惟一對象實例的接口函數
	static Singleton* GetInstance()
	{
		if(_sInstance == NUL)
		{
			if (_sInstance == NULL)
			{
				_sInstance = new Singleton();
			}
		}
		return _sInstance;
	}
// 刪除實例對象
	static void DelInstance()
	{
		if (_sInstance)
		{
			delete _sInstance;
			_sInstance = NULL;
		}
	}
	void Print()
	{
		cout<<_data<<endl;
	}
private:
	// 構造函數定義爲私有,限制只能在類內建立對象
	Singleton()
	:_data(0)
	{}
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
	// 指向實例的指針定義爲靜態私有,這樣定義靜態成員函數獲取對象實例
	static Singleton* _sInstance;
	// 單例類裏面的數據
	int _data;
};
Singleton* Singleton::_sInstance = NULL;

void TestSingleton()
{
	Singleton::GetInstance()->Print();
	Singleton::DelInstance();
}

實現二設計

線程安全的單例 -- (懶漢模式-- lazy loading)

ps: 下面部分的加鎖使用了C++11庫的互斥鎖
class Singleton
{
public:
	// 獲取惟一對象實例的接口函數
	static Singleton* GetInstance()
	{
	// 使用雙重檢查,提升效率,避免高併發場景下每次獲取實例對象都進行加鎖
		if (_sInstance == NULL)
		{
			std::lock_guard<std::mutex> lck(_mtx);
			if (_sInstance == NULL)
			{
				// tmp = new Singleton()分爲如下三個部分
				// 1.分配空間 2.調用構造函數 3.賦值
				// 編譯器編譯優化可能會把2和3進行指令重排,這樣可能會致使
				// 高併發場景下,其餘線程獲取到未調用構造函數初始化的對象
				// 如下加入內存柵欄進行處理,防止編譯器重排柵欄後面的賦值
				// 到內存柵欄以前
				Singleton* tmp = new Singleton();
				MemoryBarrier();
				_sInstance = tmp;
			}
		}
		return _sInstance;
	}
	// 刪除實例對象
	static void DelInstance()
	{
		std::lock_guard<std::mutex> lck(_mtx);
		if (_sInstance)
		{
			delete _sInstance;
			_sInstance = NULL;
		}
	}
	void Print()
	{
	cout<<_data<<endl;
	}
private:
	// 構造函數定義爲私有,限制只能在類內建立對象
	Singleton()
	:_data(0)
	{}
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
	// 指向實例的指針定義爲靜態私有,這樣定義靜態成員函數獲取對象實例
	static Singleton* _sInstance;
	// 保證線程安全的互斥鎖
	static mutex _mtx;
	// 單例類裏面的數據
	int _data;
};
Singleton* Singleton::_sInstance = NULL;
mutex Singleton::_mtx;

void TestSingleton()
{
Singleton::GetInstance()->Print();
Singleton::DelInstance();
}

實現三

線程安全的單例 -- (餓漢模式--簡潔、高效、不用加鎖、可是在某些場景下會有缺陷)

方法1

// 方式一
class Singleton
{
public:
	// 獲取惟一對象實例的接口函數
	static Singleton* GetInstance()
	{
		static Singleton sInstance;
		return &sInstance;
	}
	void Print()
	{
		cout<<_data<<endl;
	}
private:
	// 構造函數定義爲私有,限制只能在類內建立對象
	Singleton()
	:_data(0)
	{}
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
	// 單例類裏面的數據
	int _data;
};

void TestSingleton()
{
	Singleton::GetInstance()->Print();
}

方法2

// 方式二
class Singleton
{
public:
	// 獲取惟一對象實例的接口函數
	static Singleton* GetInstance()
	{
		assert(_sInstance);
		return _sInstance;
	}
	// 刪除實例對象
	static void DelInstance()
	{
		if (_sInstance)
		{
			delete _sInstance;
			_sInstance = NULL;
		}
	}
	void Print()
	{
		cout << _data << endl;
	}
private:
	// 構造函數定義爲私有,限制只能在類內建立對象
	Singleton()
	:_data(0)
	{}
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
	// 指向實例的指針定義爲靜態私有,這樣定義靜態成員函數獲取對象實例
	static Singleton* _sInstance;
	// 單例類裏面的數據
	int _data;
};
Singleton* Singleton::_sInstance = new Singleton;

void TestSingleton()
{
	Singleton::GetInstance()->Print();
	Singleton::DelInstance();
}

帶RAII GC 自動回收實例對象的方式

class Singleton
{
public:
	// 獲取惟一對象實例的接口函數
	static Singleton* GetInstance()
	{
		assert(_sInstance);
		return _sInstance;
	}
	// 刪除實例對象
	static void DelInstance()
	{
		if (_sInstance)
		{
			delete _sInstance;
			_sInstance = NULL;
		}
	}
	void Print()
	{
		cout << _data << endl;
	}
	class GC
	{
		public:
		~GC()
		{
			cout << "DelInstance()"<<endl;
			DelInstance();
		}
	};
private:
	// 構造函數定義爲私有,限制只能在類內建立對象
	Singleton()
	:_data(0)
	{}
	// 指向實例的指針定義爲靜態私有,這樣定義靜態成員函數獲取對象實例
	static Singleton* _sInstance;
	// 單例類裏面的數據
	int _data;
};
// 靜態對象在main函數以前初始化,這時只有主線程運行,因此是線程安全的。
Singleton* Singleton::_sInstance = new Singleton;
// 使用RAII,定義全局的GC對象釋放對象實例
Singleton::GC gc;
void TestSingleton()
{
	Singleton::GetInstance()->Print();
}

因爲程序在結束的時候,系統會自動析構全部的全局變量,實際上,系統也會析構全部類的靜態成員變量,就像這些靜態變量是全局變量同樣。咱們知道,靜態變量和全局變量在內存中,都是存儲在靜態存儲區的,因此在析構時,是同等對待的。


j_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gifj_0057.gif

相關文章
相關標籤/搜索