C++單例模式的六種實現

                                    (如有錯誤,請聯繫我更正,以免誤導他人!)

       C++中單例模式的應用非常廣泛,在我們平時使用的windows操作系統中就存在大量的單例應用。如:回收站、任務管理器、文件資源管理器等。

       單例模式的實現也有許許多多的方法,每種實現方法都有各自的優點和實用場景。

實現一:

<span style="font-family:SimSun;">class Singleton {
public:
	static Singleton* getInstance();
	~Singleton();
private:
	Singleton();
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
private:
	static Singleton* m_Instance;
};
Singleton* Singleton::m_Instance = nullptr;
Singleton::Singleton() {}
Singleton::~Singleton() {}
Singleton* Singleton::getInstance() {
	if (m_Instance == nullptr)
		m_Instance = new Singleton;
	return m_Instance;
}</span>


分析

       優點:1、實例在首次調用getInstance時才創建

       缺點:1、程序結束時析構函數不被調用,需要手動delete釋放內存並關閉/恢復類中操作(主要是程序結束後還是不被關閉的操作,如:數據庫連接、文件鎖、註冊表操作)

       2、線程不安全,在多線程情況下可能會產生多個實例(下面是break在QQ剛登入沒多久時查看的線程數,82個。。。試想如果82個線程同時調用getInstance)

實現二:在實現一的基礎上修改getInstance函數如下:

<span style="font-family:SimSun;font-size:18px;">Singleton* Singleton::getInstance() {
	if (m_Instance == nullptr)
	{
		Lock();//線程鎖
		if (m_Instance == nullptr)
			m_Instance = new Singleton;
		UnLock();
	}
	return m_Instance;
}</span>

分析

       在實現一的基礎上解決了線程不安全問題,但加鎖操作使程序在性能上有所下降(當數據量大的時候不推薦使用)

實現三:

<span style="font-family:SimSun;font-size:18px;">class Singleton {
public:
	static Singleton* getInstance();
	~Singleton();
private:
	Singleton();
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
};
Singleton::Singleton() {}
Singleton::~Singleton() {}
Singleton* Singleton::getInstance() {
	static Singleton m_Instance;
	return &m_Instance;
}</span>

分析

       使用了局部靜態變量。在實現二的基礎上解決了性能下降的問題,但實例只能在程序結束時銷燬,從程序開始到程序結束一直佔用內存。換一種思考方式,相對於前面兩種實現,實現三能夠省去程序員的維護工作,不用手動delete釋放內存。

實現四:

<span style="font-family:SimSun;font-size:18px;">class Singleton {
public:
	static Singleton* getInstance();
	~Singleton();
private:
	Singleton();
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
	static Singleton m_Instance;
};
Singleton Singleton::m_Instance;
Singleton::Singleton() {}
Singleton::~Singleton() {}
Singleton* Singleton::getInstance() {
	return &m_Instance;
}</span>

分析

       與實現三相比,實現四中實例爲靜態成員變量,在程序的開始初始化。

實現五:

<span style="font-family:SimSun;font-size:18px;">class Singleton {
public:
	static Singleton* getInstance();
	static void destoryInstance();
private:
	~Singleton();
	Singleton();
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
	static Singleton* const m_Instance;
};
Singleton* const Singleton::m_Instance=new Singleton;
Singleton::Singleton() {}
Singleton::~Singleton() {}
void Singleton::destoryInstance() {
	if (m_Instance != nullptr)
	{
		delete m_Instance;
		const_cast<Singleton*>(m_Instance) = nullptr;
	}
}
Singleton* Singleton::getInstance() {
	return m_Instance;
}</span>

分析

       實例在程序開始初始化,避免了實現一中的線程不安全問題。同實現一、二,相對於實現三、四而言,我們需要手動釋放內存。

實現六:

<span style="font-family:SimSun;font-size:18px;">class Singleton {
public:
	static Singleton* getInstance();
	static void destoryInstance();
private:
	~Singleton();
	Singleton();
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
private:
	class GC {
	public:
		~GC()
		{
			destoryInstance();
		}
	};
private:
	static Singleton* const m_Instance;
	static GC gc;
};
Singleton* const Singleton::m_Instance = new Singleton;
Singleton::GC Singleton::gc;
Singleton::Singleton() {}
Singleton::~Singleton() {}
void Singleton::destoryInstance() {
	if (m_Instance != nullptr)
	{
		delete m_Instance;
		const_cast<Singleton*>(m_Instance) = nullptr;
	}
}
Singleton* Singleton::getInstance() {
	return m_Instance;
}</span>

分析

       在實現五的基礎上添加了私有內部類GC,創建了私有靜態成員變量gc。這裏利用了C++的RALL機制。當程序結束時,通過釋放靜態成員變量gc來釋放單例m_Instance,並在單例的析構中做一些對類中其他操作的釋放。這裏相對於實現五來說添加了實現三、四具備的自動回收機制。利用實現六,我們可以在需要釋放的時候手動釋放單例,也可以讓程序在結束時自動回收單例(防止程序員忘記釋放內存,就像我們平時忘記delete一樣)。我們在實現一個有很多不可預料的因素的功能時,這麼做是很有幫助的,我們可以通過對不可預測的因素進行if判斷來決定是否提前釋放單例。


       可見單例模式的實現多種多樣,各有千秋。我們需要根據功能需求來選擇合適我們的實現方法


       如果有更多更好的單例模式實現方法,小夥伴們不妨拿出來分享一下,謝謝啦!



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