設計模式學習筆記-----單例模式

  • 爲什麼使用單例模式

大規模系統中,爲了性能的考慮,需要節省對象的創建時間等等。因爲Singleton模式可以保證爲一個類只生成唯一的實例對象,所以這些情況,Singleton模式就派上用場了。比如使用Socket時,只需要一個Socket對象即可,這時候單例模式就有實戰之地了。

  • 實現單例步驟常用步驟

  1. 構造函數私有化
  2. 提供一個全局的靜態方法(全局訪問點)

  3. 在類中定義一個靜態指針,指向本類的變量的靜態變量指針

單例模式分類

懶漢式、餓漢式

  • 懶漢式單例模式普通寫法  單線程下
Singelton.h
#include <iostream>
using namespace std;

class Singelton
{
private:
	Singelton();
public:
	virtual ~Singelton();
public:
	static Singelton* getInstance();
	void freeInstance();
	static void printT();
private:
	static Singelton *m_Singleton;
	static int m_count;
};

Singelton*	Singelton::m_Singleton = nullptr;
int			Singelton::m_count = 0;

----------------------------------------------------------
Singelton.cpp
#include "Singelton.h"
Singelton::Singelton()
{
	m_Singleton = nullptr;
	m_count = 0;
}

Singelton::~Singelton()
{
}
/**只有當使用的時候纔會將對象創建出來**/
Singelton* Singelton::getInstance()
{
	/*單線程下該寫法沒有問題,多線程有問題*/
	if (m_Singleton == nullptr) 
	{
		m_Singleton = new Singelton();
	}
	return m_Singleton;
}

void Singelton::freeInstance() 
{
	if (m_Singleton != nullptr) 
	{
		delete m_Singleton;
		m_Singleton = nullptr;
		m_count = 0;
	}
}

void Singelton::printT()
{
	cout << "m_count: " << m_count << endl;
}

main.cpp

#include"Singelton.h"
int main()
{
	Singelton* p1 = Singelton::getInstance();
	Singelton* p2 = Singelton::getInstance();
	if (p1 != p2)
	{
		cout << "不是同一個對象" << endl;
	}
	else
	{
		cout << "是同一個對象" << endl;
	}
	p1->printT();
	p2->printT();
	system("pause");
        Singelton::freeInstance();
	return 0;
}
  • 懶漢式單例模式多線程下寫法

異同點:單線程的寫法使用到多線程後,每次調用GetInstance()靜態方法時,必須判斷 m_Singleton == nullptr,使程序相對開銷增大。在多線程中,該寫法會導致多個實例的產生,從而導致運行代碼不正確以及內存的泄露。

原因:因爲C++中構造函數並不是線程安全的 ,C++中的構造函數簡單來說分兩步:內存分配 、初始化成員變量。由於多線程的關係,可能當我們在分配內存好了以後,還沒來得急初始化成員變量就進行線程切換,另外一個線程拿到所有權後,由於內存已經分配了,但是變量初始化還沒進行,因此打印成員變量的相關值會發生不一致現象。

但是當使用懶漢式處理大數據時,此處的鎖卻變成了影響性能的關鍵因素。

//多線程優化單例模式
class Singelton
{
private:
	Singelton();
	//防止拷貝構造和賦值操作
	Singelton(const Singelton&) { ; }
	Singelton& operator=(const Singelton &obj) { ; }
public:
	virtual ~Singelton();
public:
	
	static Singelton* getInstance();
	static void freeInstance();
	void printT();
private:
	static Singelton *m_Singleton;
	static std::mutex m_mutex;
	static int m_count;
};

-----------------------------------------------------------
Singelton*	Singelton::m_Singleton = nullptr;
int			Singelton::m_count = 0;
std::mutex  Singelton::m_mutex;
Singelton::Singelton()
{
	
	m_Singleton = nullptr;
	m_count = 0;
}

Singelton::~Singelton()
{
	
}
/**只有當使用的時候纔會將對象創建出來**/
Singelton* Singelton::getInstance()
{
	if (m_Singleton == nullptr)  //double check 
	{
		//只有當m_Singleton等於null時,纔開始使用加鎖機制 二次檢查
		m_mutex.try_lock();	
		if (m_Singleton == nullptr)
		{
			m_Singleton = new Singelton();
		}
		m_mutex.unlock();
	}
	return m_Singleton;
}

void Singelton::freeInstance()
{
	if (m_Singleton != nullptr)
	{
		delete m_Singleton;
		m_Singleton = nullptr;
		m_count = 0;
	}
}

void Singelton::printT()
{
	std::cout << "m_count: " << m_count << std::endl;
}

瞭解了懶漢式單例模式的創建方式後,再來看餓漢式對比度就顯而易見了,,餓漢式就是不管有沒有人要這個實例對象,都先把實例對象創建出來。

//餓漢式單例模式
class Singelton
{
private:
	Singelton();
	//防止拷貝構造和賦值操作
	Singelton(const Singelton&) { ; }
	Singelton& operator=(const Singelton &obj) { ; }
public:
	virtual ~Singelton();
public:

	static Singelton* getInstance();
	static void freeInstance();
private:
	static Singelton *m_Singleton;
};

Singelton* Singelton::m_Singleton = new Singelton();

Singelton::Singelton()
{
}

Singelton::~Singelton()
{

}
/**只有當使用的時候纔會將對象創建出來**/
Singelton* Singelton::getInstance()
{
	return m_Singleton;
}

void Singelton::freeInstance()
{
	if (m_Singleton != nullptr)
	{
		delete m_Singleton;
		m_Singleton = nullptr;
	}
}

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章