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

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

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

特殊類設(shè)計(jì)

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

請(qǐng)?jiān)O(shè)計(jì)一個(gè)類,只能在堆上創(chuàng)建對(duì)象

實(shí)現(xiàn)方式:

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

請(qǐng)?jiān)O(shè)計(jì)一個(gè)類只能在棧上創(chuàng)建對(duì)象

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

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

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

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

請(qǐng)?jiān)O(shè)計(jì)一個(gè)類不能被拷貝

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

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

原因:

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

請(qǐng)?jiān)O(shè)計(jì)一個(gè)類不能被繼承

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

class test
{
private:
	test(){}
}

C++11方法

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

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

請(qǐng)?jiān)O(shè)計(jì)一個(gè)類只能創(chuàng)建一個(gè)對(duì)象(單例模式)

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

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

單例模式:

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

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

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

實(shí)現(xiàn)方法:在類里面定義一個(gè)靜態(tài)的成員指針obj,通過提供一個(gè)靜態(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è)計(jì)模式,單例對(duì)象構(gòu)造十分耗時(shí)或者占用很多資源,比如加載插件啊, 初始化網(wǎng)絡(luò)連接啊,讀取文件啊等等,而有可能該對(duì)象程序運(yùn)行時(shí)不會(huì)用到,那么也要在程序一開始就進(jìn)行初始化,就會(huì)導(dǎo)致程序啟動(dòng)時(shí)非常的緩慢。 所以這種情況使用懶漢模式(延遲加載)更好。

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

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

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

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

進(jìn)一步優(yōu)化

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

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

完善實(shí)現(xiàn)

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

懶漢模式和餓漢模式的對(duì)比

餓漢

優(yōu)點(diǎn):簡(jiǎn)單

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

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

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

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

懶漢

優(yōu)點(diǎn):解決上面餓漢的缺點(diǎn)。因?yàn)樗堑谝淮握{(diào)用GetInstance時(shí)創(chuàng)建初始化單例對(duì)象

缺點(diǎn):相對(duì)餓漢,復(fù)雜一點(diǎn)點(diǎn)。

迭代器模式、適配器模式

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

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

相關(guān)文章

  • STL容器之vector源碼詳細(xì)解讀

    STL容器之vector源碼詳細(xì)解讀

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

    C/C++新建注冊(cè)表項(xiàng)的代碼示例

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

    C語言中求和、計(jì)算平均值、方差和標(biāo)準(zhǔn)差的實(shí)例

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

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

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

    EasyC++模板顯式具體化

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

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

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

    MFC對(duì)話框自定義消息映射的方法

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

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

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

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

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

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

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

最新評(píng)論