欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++單例設(shè)計模式詳細講解

 更新時間:2022年06月10日 10:47:28   作者:愛生活,愛代碼  
單例模式(Singleton Pattern)是最簡單的設(shè)計模式之一。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式,這種模式涉及到一個單一的類,該類負責(zé)創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建

特殊類設(shè)計

只能在堆上創(chuàng)建對象的類

請設(shè)計一個類,只能在堆上創(chuàng)建對象

實現(xiàn)方式:

  • 將類的構(gòu)造函數(shù)私有,拷貝構(gòu)造聲明成私有。防止別人調(diào)用拷貝在棧上生成對象。
  • 提供一個靜態(tài)的成員函數(shù),在該靜態(tài)成員函數(shù)中完成堆對象的創(chuàng)建
class test 
{
public:
	static test* GetObj() 
	{
		return new test();  //堆上申請并創(chuàng)建一個對象
	}
private:
	//構(gòu)造函數(shù)私有
	test() { cout << "調(diào)用了構(gòu)造函數(shù)" << endl; }
	//拷貝構(gòu)造私有化,無法實例化對象
	test(const test& obj){};
};
void func1() 
{
	test* p = test::GetObj();  //通過調(diào)用靜態(tài)函數(shù)獲取對象的指針
}

請設(shè)計一個類只能在棧上創(chuàng)建對象

方法一:同上將構(gòu)造函數(shù)私有化,然后設(shè)計靜態(tài)方法創(chuàng)建對象返回即可。

//只能在棧上創(chuàng)建對象
class test1 
{
public:
	static test1 GetObj()
	{
		return test1(); //棧上創(chuàng)建一個對象并返回
	}
	test1() { cout << "調(diào)用了構(gòu)造函數(shù)" << endl; }
private:
	//拷貝構(gòu)造私有化無法實例化對象
	test1(const test1& obj) {  }
};

方法二:屏蔽new,因為new在底層調(diào)用void* operator new(size_t size)函數(shù)(會在堆上開辟空間),我們只需要在類里面自定義定位new和delete就會不再new的時候調(diào)用全局的operator new和operator delete 最后該函數(shù)私有化,也就防止了在堆上創(chuàng)建對象,注意:也要防止定位new

class test02 
{
public:
	test02() 
	{ }
private:
	//自定義的定位new、定位delete
	void *operator new(size_t size) 
	{ 
		//代碼....  
	}
	void operator delete(void *p)  
	{  
		//代碼....
	}
};

請設(shè)計一個類不能被拷貝

拷貝只會放生在兩個場景中:拷貝構(gòu)造函數(shù)以及賦值運算符重載,因此想要讓一個類禁止拷貝,只需讓該類不能調(diào)用拷貝構(gòu)造函數(shù)以及賦值運算符重載即可。

C++98將拷貝構(gòu)造函數(shù)與賦值運算符重載只聲明不定義,并且將其訪問權(quán)限設(shè)置為私有即可

原因:

  • 設(shè)置成私有:如果只聲明沒有設(shè)置成private,用戶自己如果在類外定義了,就可以不能禁止拷貝了
  • 只聲明不定義:不定義是因為該函數(shù)根本不會調(diào)用,定義了其實也沒有什么意義,不寫反而還簡單,而且如果定義了就不會防止成員函數(shù)內(nèi)部拷貝了。
  • C++11的寫法也可以在 默認成員函數(shù)后面加上delete,表示讓編譯器刪除掉該默認成員函數(shù)
class CopyBan
{
 // ...
private:
 CopyBan(const CopyBan&);
 CopyBan& operator=(const CopyBan&);
 //...
};

請設(shè)計一個類不能被繼承

C++98會對父類的構(gòu)造函數(shù)私有化,但是這個方法并不夠徹底,實際上是子類還是繼承了父類的,只是沒辦法實例化對象,那么也就沒有意義了

class test
{
private:
	test(){}
}

C++11方法

final關(guān)鍵字,final修飾類,表示該類不能被繼承

class A final
{
 // ....
};

請設(shè)計一個類只能創(chuàng)建一個對象(單例模式)

設(shè)計模式(Design Pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類的、代碼設(shè)計經(jīng)驗的總結(jié)。為什么會產(chǎn)生設(shè)計模式這樣的東西呢?就像人類歷史發(fā)展會產(chǎn)生兵法。最開始部落之間打仗時都是人拼人的對砍。后來春秋戰(zhàn)國時期,七國之間經(jīng)常打仗,就發(fā)現(xiàn)打仗也是有套路的,后來孫子就總結(jié)出了《孫子兵法》。孫子兵法也是類似。

使用設(shè)計模式的目的:為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性。 設(shè)計模式使代碼編寫真正工程化;設(shè)計模式是軟件工程的基石脈絡(luò),如同大廈的結(jié)構(gòu)一樣。

單例模式:

一個類只能創(chuàng)建一個對象,即單例模式,該模式可以保證系統(tǒng)中該類只有一個實例,并提供一個訪問它的全局訪問點,該實例被所有程序模塊共享。比如在某個服務(wù)器程序中,該服務(wù)器的配置信息存放在一個文件中,這些配置數(shù)據(jù)由一個單例對象統(tǒng)一讀取,然后服務(wù)進程中的其他對象再通過這個單例對象獲取這些配置信息,這種方式簡化了在復(fù)雜環(huán)境下的配置管理。

單例模式有兩種實現(xiàn)模式:

餓漢模式、就是說不管你將來用不用,程序啟動時就創(chuàng)建一個唯一的實例對象,如果這個單例對象在多線程高并發(fā)環(huán)境下頻繁使用,性能要求較高,那么顯然使用餓漢模式來避免資源競爭,提高響應(yīng)速度更好。

實現(xiàn)方法:在類里面定義一個靜態(tài)的成員指針obj,通過提供一個靜態(tài)的成員函數(shù)獲取該指針obj

class Singleton 
{
public:
	static Singleton* GetInstance()
	{
		return obj;
	}
private:
	Singleton() {}
	Singleton(const Singleton& obj) {}
	static Singleton* obj;
};
Singleton* Singleton::obj = new Singlet	on; 

懶漢模式、如果在套用單例設(shè)計模式,單例對象構(gòu)造十分耗時或者占用很多資源,比如加載插件啊, 初始化網(wǎng)絡(luò)連接啊,讀取文件啊等等,而有可能該對象程序運行時不會用到,那么也要在程序一開始就進行初始化,就會導(dǎo)致程序啟動時非常的緩慢。 所以這種情況使用懶漢模式(延遲加載)更好。

懶漢模式:我們需要考慮到多線程的安全隱患,和處理的返回值指針指向的永遠都是同一個對象的指針,這樣子才不會失去單例對象的性質(zhì),主要改變的是static Singleton* GetInstance();接口函數(shù)的設(shè)計

static Singleton* GetInstance()
{
	//如果只有一層的if判斷的我們的程序就會存在一定的問題,
	//在多線程的場景下,多個線程都是比較自由的,可讀可寫、或者只讀、只寫
	//這樣在一些場景下就會導(dǎo)致單例對象失去他本身的性質(zhì),
	//比如:兩個線程 th1、th2,同時進入到了if語句中,假設(shè)th1會先執(zhí)行。
	//為obj 申請了一個對象并返回它的指針,難么這個實例對象也就創(chuàng)建了
	//最后return obj,th1結(jié)束后(不確定結(jié)束時間),
	//th2又會再次new一個對象給obj,最后導(dǎo)致obj被賦值兩次
	//整個過程梳理:
	//1、th1線程存在內(nèi)存泄漏,因為obj被賦值兩次,而第一次new出來的對象并沒有被釋放,那么就存在了內(nèi)存泄漏
	//2、obj被賦值兩次,失去單例對象性質(zhì)
	if (!obj) 
	{
		if (!obj) 
		{
			obj = new Singleton();
		}
	}
	return obj;  //返回對象的指針
}

改善一:引入C++11線程庫,加入互斥鎖管理線程

static Singleton* GetInstance()
{
		//通過加鎖,保證了線程安全
		m_mtx.lock();  //加鎖
		//假設(shè)th1先拿到鎖,那么th1就會先執(zhí)行下面的語句,而th2就會等待,
		//obj就會指向new出來的單例對象,最后th1解鎖完了return ,而th2
		//才剛拿到鎖繼續(xù)if判斷的時候obj已經(jīng)有值了就會跳過if繼續(xù)往下執(zhí)
		//行,然后解鎖最終return ,整個過程中單例對象只有一份,
		//不存在二次賦值
		if (!obj) 
		{
			obj = new Singleton();
		}
		m_mtx.unlock();  //解鎖
	return obj;  //返回對象的指針
}

進一步優(yōu)化

因為第一次加鎖解鎖之后,處理了線程安全的問題,而往后的obj指針已經(jīng)被初始化了,也就不需要再new一次,所以可以再最外層套上一層if判斷,防止繼續(xù)枷鎖解鎖,因為頻繁的加鎖解鎖會導(dǎo)致線程不斷的切入切出有上下文切換的開銷

static Singleton* GetInstance()
{
	if(!obj)  
		//第一次判斷保護線程安全,第二次obj已經(jīng)有值,不需要執(zhí)行{....}
		//優(yōu)點:保證線程安全的同時完成了單例對象的初始化
	{
		m_mtx.lock();  //加鎖
		if (!obj)   //保證單例對象只有一份,不會存在二次賦值
		{
			obj = new Singleton();
		}
		m_mtx.unlock();  //解鎖
	}
	return obj;  //返回對象的指針
}

完善實現(xiàn)

//懶漢
class Singleton 
{
public:
	class CGarbo {
	public:
		~CGarbo() {
			if (obj)   //釋放對象指針,并置空
			{
				delete obj;
				obj = nullptr;
			}
		}
	};
public:
	static Singleton* GetInstance()
	{
		//雙重檢查的好處是保護了線程安全,同時又提高了效率
		if (!obj) 
		{
			m_mtx.lock();  //加鎖
			if (!obj) 
			{
				obj = new Singleton();
			}
			m_mtx.unlock();  //解鎖
		}
		return obj;  //返回對象的指針
	}
private:
	Singleton()  {}
	Singleton(const Singleton& obj) {}
	void operator=(const Singleton& obj) {}
	static Singleton* obj; //聲明對象指針
	static mutex m_mtx; //聲明互斥鎖
	static CGarbo Garbo;  //聲明垃圾回收器對象
};
Singleton* Singleton::obj = nullptr;  //對象指針初始化
mutex Singleton::m_mtx;  //定義互斥鎖
Singleton::CGarbo Garbo;  //定義垃圾回收器對象

懶漢模式和餓漢模式的對比

餓漢

優(yōu)點:簡單

缺點:1、如果單例對象構(gòu)造函數(shù)工作比較多,會導(dǎo)致程序啟動慢,遲遲進不了入口main函數(shù)

2、如果有多個單例對象,他們之間有初始化依賴關(guān)系,餓漢模式也會有問題。

比如有A和B兩個單例類,要求A單例先初始化,B必須在A之后初始化。那么餓漢無法保證

這種場景下面用懶漢就可以,懶漢可以先調(diào)用A::GetInstance(),再調(diào)用B::GetInstance().

懶漢

優(yōu)點:解決上面餓漢的缺點。因為他是第一次調(diào)用GetInstance時創(chuàng)建初始化單例對象

缺點:相對餓漢,復(fù)雜一點點。

迭代器模式、適配器模式

設(shè)計模式有興趣的同學(xué),可以下去再看看工廠模式、觀察者模式等等

到此這篇關(guān)于C++單例設(shè)計模式詳細講解的文章就介紹到這了,更多相關(guān)C++單例設(shè)計內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • STL容器之vector源碼詳細解讀

    STL容器之vector源碼詳細解讀

    這篇文章主要介紹了STL容器之vector源碼詳細解讀,vector的數(shù)據(jù)安排和array和類似,它們的主要差別在于空間的運用和靈活性,array是靜態(tài)空間,一旦配置了就不能改變,需要的朋友可以參考下
    2024-01-01
  • C/C++新建注冊表項的代碼示例

    C/C++新建注冊表項的代碼示例

    今天小編就為大家分享一篇關(guān)于C/C++新建注冊表項的代碼示例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • C語言中求和、計算平均值、方差和標準差的實例

    C語言中求和、計算平均值、方差和標準差的實例

    這篇文章主要介紹了C語言中求和、計算平均值、方差和標準差的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • C++實現(xiàn)公司人事管理系統(tǒng)

    C++實現(xiàn)公司人事管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C++實現(xiàn)公司人事管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • EasyC++模板顯式具體化

    EasyC++模板顯式具體化

    這篇文章主要介紹了C++模板顯式具體化,在C++中,可以提供一個具體化函數(shù)定義稱為具體顯式化(explict?specialization)。其中包含所需的代碼,當(dāng)編譯器找到與函數(shù)調(diào)用匹配的具體化定義時,將使用該定義,而不再尋找模板,下面我們來看看文章具體介紹吧
    2021-12-12
  • 用c語言根據(jù)可變參數(shù)合成字符串的實現(xiàn)代碼

    用c語言根據(jù)可變參數(shù)合成字符串的實現(xiàn)代碼

    本篇文章是對用c語言根據(jù)可變參數(shù)合成字符串的方法進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • MFC對話框自定義消息映射的方法

    MFC對話框自定義消息映射的方法

    這篇文章主要介紹了MFC對話框自定義消息映射的方法,實例分析了MFC自定義消息映射的消息定義、響應(yīng)、聲明及實現(xiàn)消息映射的相關(guān)技巧,需要的朋友可以參考下
    2015-07-07
  • Cocos2d-x中使用CCScrollView來實現(xiàn)關(guān)卡選擇實例

    Cocos2d-x中使用CCScrollView來實現(xiàn)關(guān)卡選擇實例

    這篇文章主要介紹了Cocos2d-x中使用CCScrollView來實現(xiàn)關(guān)卡的選擇實例,本文在代碼中用大量注釋講解了CCScrollView的使用,需要的朋友可以參考下
    2014-09-09
  • OpenCV實現(xiàn)給圖片添加邊框功能

    OpenCV實現(xiàn)給圖片添加邊框功能

    這篇文章主要為大家詳細介紹了OpenCV實現(xiàn)給圖片添加邊框功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • Qt編寫地圖遷徙圖的實現(xiàn)示例

    Qt編寫地圖遷徙圖的實現(xiàn)示例

    本文主要介紹了Qt編寫地圖遷徙圖的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12

最新評論