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

C++深入詳解單例模式與特殊類設(shè)計(jì)的實(shí)現(xiàn)

 更新時(shí)間:2022年06月13日 14:27:47   作者:你算哪一個(gè)bug?  
這篇文章主要為大家詳細(xì)介紹了C++單例模式和特殊類的設(shè)計(jì),單例模式這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

單例模式

什么是單例模式

單例模式(Singleton),保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。–大話設(shè)計(jì)模式

應(yīng)用場(chǎng)景

保證一個(gè)類只有一個(gè)實(shí)例

  • 如Windows下的任務(wù)管理器,回收站等。
  • 日志管理,計(jì)數(shù)器等。

簡(jiǎn)而言之,你需要唯一實(shí)例時(shí)就可以考慮單例模式。這樣它可以嚴(yán)格地控制客戶怎樣訪問即何時(shí)訪問它,即對(duì)唯一實(shí)例的受控訪問。

優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 減少內(nèi)存開銷,因?yàn)樵谙到y(tǒng)中只有一個(gè)實(shí)例。
  • 避免了頻繁的創(chuàng)建和銷毀對(duì)象,提高了性能
  • 避免對(duì)資源的多重占用,比如單例時(shí)多人只寫一個(gè)日志文件,如果有多個(gè)日志文件可能導(dǎo)致對(duì)相同的日志文件進(jìn)行寫操作
  • 設(shè)置全局訪問點(diǎn)

缺點(diǎn)

  • 職責(zé)過重,與單一職責(zé)存在沖突
  • 不能繼承(構(gòu)造方法私有)

實(shí)現(xiàn)

單例模式有兩種實(shí)現(xiàn)模式,懶漢模式和餓漢模式。

注意單例模式的概念,大概就是唯一實(shí)例且有全局訪問點(diǎn),我們從這兩點(diǎn)入手。

唯一實(shí)例:構(gòu)造函數(shù)私有+防拷貝

拷貝構(gòu)造是一種構(gòu)造方式,所以需要防止,構(gòu)造函數(shù)私有不讓別人new。

全局訪問點(diǎn):給一個(gè)公共的接口

餓漢模式

簡(jiǎn)單來說,把東西一開始就做好,需要的時(shí)候直接吃。

#include <iostream>
using namespace std;
class Singleton
{
public:
	static Singleton& GetInstance()
	{
		return _instance;
	}
	int GetRandom()//由于側(cè)重點(diǎn)不是隨機(jī)數(shù) 所以直接返回一個(gè)30
	{
		return 30;
	}
private:
	Singleton() {}//構(gòu)造函數(shù)私有
	Singleton(Singleton&) = delete;//防拷貝,被=delete修飾表明這個(gè)函數(shù)被刪除,即可以只聲明不實(shí)現(xiàn),換言之禁用了該函數(shù)
	Singleton& operator=(const Singleton&) = delete;//防拷貝
	static Singleton _instance;
};
Singleton Singleton::_instance;//類外必須初始化,類內(nèi)只是聲明
int main()
{
	//1.調(diào)用
	cout<<Singleton::GetInstance().GetRandom()<<endl;
	//2.
	Singleton& s = Singleton::GetInstance();
	cout<<s.GetRandom()<<endl;
	return 0;
}

通過防拷貝和構(gòu)造函數(shù)私有化之后下面的幾種辦法都失效了

Singleton s;//err
Singleton s1(s);//err
Singleton s1=s;//err

從上面我們可以看出餓漢模式的優(yōu)缺點(diǎn)了,有點(diǎn)顯而易見就是實(shí)現(xiàn)很簡(jiǎn)單粗暴,缺點(diǎn)很明顯,類加載時(shí)單例對(duì)象就已經(jīng)生成了,即還沒有用就已經(jīng)加載出來了,比如這個(gè)資源很大,又在游戲啟動(dòng)時(shí)加載,那就會(huì)造成游戲啟動(dòng)很慢。且如果有多個(gè)單例對(duì)象啟動(dòng)時(shí)實(shí)例化順序不確定(不同源文件類內(nèi)的單例對(duì)象實(shí)例化順序是不確定的,懶漢模式解決了這個(gè)問題,因?yàn)閼袧h模式的實(shí)例化在函數(shù)內(nèi)部,可以通過調(diào)用函數(shù)的順序來解決實(shí)例化的順序問題)。

類加載時(shí)靜態(tài)初始化解決了線程安全問題。

懶漢模式

什么時(shí)候需要就什么時(shí)候做飯,然后吃。

class Singleton
{
public:
	static Singleton* GetInstance()
	{
		if (_instance == nullptr)
		{
			_mtx.lock();//double lock保證線程安全
			if (_instance == nullptr)//必須再次檢查  不然可能另一個(gè)線程那已經(jīng)new完了,這邊又new違背了單例,且可能覆蓋數(shù)據(jù)。
			{
				_instance = new Singleton();
			}
			_mtx.unlock();
		}
		return _instance;
	}
	int GetRandom()
	{
		return 30;
	}
	class Clear//資源回收的內(nèi)部類,必須是公有的,不然外部聲明報(bào)錯(cuò)
	{
	public:
		~Clear()
		{
			cout << "釋放資源" << endl;
			delete _instance;
		}
	};
private:
	static Clear _cle;
	Singleton() {}
	Singleton(Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
	static Singleton* _instance;
	static mutex _mtx;//線程安全
};
Singleton* Singleton::_instance = nullptr;
mutex Singleton::_mtx;
Singleton::Clear _cle;
int main()
{	
	//1.
	cout<<Singleton::GetInstance()->GetRandom()<<endl;
	//2/
	static Singleton* s = Singleton::GetInstance();
	cout << s->GetRandom() << endl;
	//不能用左值引用接收的原因  返回值是右值 得用右值引用接收
	//為什么函數(shù)返回值是右值  因?yàn)榉祷刂凳墙柚R時(shí)變量返回的拿不到地址
	//如果返回值是左值引用就能用左值引用接收
	return 0;
}

懶漢模式的優(yōu)缺點(diǎn)也很明顯,優(yōu)點(diǎn)是什么時(shí)候第一次用就什么時(shí)候?qū)嵗?,此外可以通過調(diào)用函數(shù)解決多個(gè)單例對(duì)象實(shí)例化的順序問題,缺點(diǎn)就是寫起來復(fù)雜,要考慮線程安全和內(nèi)存泄露方面的問題。懶漢采用的是指針也更好回收資源(餓漢采用的是對(duì)象)

懶漢可能因?yàn)槎嗑€程丟數(shù)據(jù),線程加鎖保證在多線程環(huán)境下一定只有一個(gè)線程去new對(duì)象,只創(chuàng)建出一個(gè)單例對(duì)象,加鎖可能導(dǎo)致頻繁切換上下文,double lock解決。

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

我們一般會(huì)通過構(gòu)造函數(shù),拷貝構(gòu)造和賦值重載創(chuàng)建對(duì)象,現(xiàn)在把這幾種方法全禁用了,然后自己再寫一個(gè)外部可調(diào)用的接口來自定義類的創(chuàng)建方式。

當(dāng)然也有別的辦法,這種經(jīng)??梢宰鳛橐环N通解思路。

下面的禁用采用C++11的delete關(guān)鍵字實(shí)現(xiàn),作用是禁止編譯器生成默認(rèn)的函數(shù)版本,即有聲明但無實(shí)現(xiàn)。

設(shè)計(jì)一個(gè)類只能在堆上創(chuàng)建對(duì)象

簡(jiǎn)單來說只能通過new來創(chuàng)建對(duì)象。

方法一

構(gòu)造方法禁用,然后給一個(gè)接口讓外部調(diào)用。

構(gòu)造方法的禁用=構(gòu)造函數(shù)私有+禁用拷貝構(gòu)造+禁用賦值重載

class HeapOnly
{
public:
	static HeapOnly* GetInstance()
	{
		return new HeapOnly;
	}
	void Test()
	{
		cout << "I am Test" << endl;
	}
private:
	HeapOnly() {}
	HeapOnly(HeapOnly&) = delete;
	HeapOnly& operator=(const HeapOnly&) = delete;
};
int main()
{	
	HeapOnly* ho = HeapOnly::GetInstance();
	ho->Test();
	delete ho;
	return 0;
}

方法二

析構(gòu)函數(shù)私有

對(duì)象建立在棧上,是由編譯器分配空間的,編譯器管理對(duì)象的生命周期,對(duì)象使用完后編譯器會(huì)檢查這個(gè)對(duì)象所有的非靜態(tài)函數(shù),包括析構(gòu)函數(shù),當(dāng)編譯器發(fā)現(xiàn)析構(gòu)函數(shù)不能訪問后就不能回收這塊空間,所以編譯器無法為其分配空間,編譯器檢查到這種情況也會(huì)報(bào)錯(cuò)。

析構(gòu)函數(shù)私有的方法不建議使用,因?yàn)樵陬愅鉄o法使用delete釋放空間,容易造成內(nèi)存泄漏。

class HeapOnly
{
public:
	void Test()
	{
		cout << "I am Test" << endl;
	}
private:
	~HeapOnly();
};
int main()
{	
	//HeapOnly ho_stack;//err
	HeapOnly* ho = new HeapOnly;
	ho->Test();
	return 0;
}

只能在棧上創(chuàng)建對(duì)象

方法一

不能使用new --> 重載operator new即可。

這種存在一個(gè)缺陷,就是仍然可以在靜態(tài)區(qū)創(chuàng)建對(duì)象

方法二

將構(gòu)造函數(shù)設(shè)為私有再自定義一個(gè)接口

這里不用禁用構(gòu)造函數(shù),拷貝構(gòu)造,賦值重載,因?yàn)閷?duì)于下面的場(chǎng)景來說,匿名對(duì)象的拷貝構(gòu)造更符合場(chǎng)景,編譯器會(huì)選拷貝構(gòu)造來進(jìn)行構(gòu)造,所以如果我們禁用了拷貝構(gòu)造會(huì)報(bào)錯(cuò),因?yàn)槲覀兊膁elete關(guān)鍵字是有聲明無實(shí)現(xiàn),并不是真的把這個(gè)函數(shù)連帶聲明刪除了。所以StackOnly()一看有我們自己寫的拷貝構(gòu)造的聲明就會(huì)去匹配這個(gè)拷貝構(gòu)造,我們自己寫的拷貝構(gòu)造又沒有實(shí)現(xiàn)拷貝功能就會(huì)報(bào)錯(cuò)。

理解這個(gè)和編譯鏈接知識(shí)有關(guān),編譯器看到聲明就去匹配了,而不是一定要看到函數(shù)實(shí)現(xiàn)才匹配,鏈接時(shí)候才會(huì)去找實(shí)現(xiàn),當(dāng)發(fā)現(xiàn)有聲明無實(shí)現(xiàn)時(shí)就很容易導(dǎo)致鏈接錯(cuò)誤。

一個(gè)類不能被繼承

父類構(gòu)造函數(shù)私有即可,構(gòu)造時(shí)先構(gòu)造父類再構(gòu)造子類,父類構(gòu)造不出不能繼承。

最后

關(guān)于單例模式,可以說是只能創(chuàng)建一個(gè)對(duì)象的實(shí)現(xiàn)。

還有個(gè)小問題,為什么不用全局變量代替單例模式,全局定義一個(gè)唯一的變量不就行了,合理吧[doge],理論上可以,但是非常不建議,可以自行查找資料"為什么不建議使用全局變量"。

全局變量的使用可能帶來很多問題,而且很容易造成鏈接、重定義等錯(cuò)誤,如果多人協(xié)作時(shí)有人再給這個(gè)變量起個(gè)別名,那維護(hù)代碼時(shí)代價(jià)就太大了,這還只是全局變量的一個(gè)小缺點(diǎn)。

??痛a規(guī)范評(píng)分里也不建議用全局變量,當(dāng)然寫題時(shí)代碼沒那么長(zhǎng),幾個(gè)全局變量倒時(shí)不打緊。

.h不能包含定義,不然多個(gè)cpp去包含就會(huì)有鏈接錯(cuò)誤,盡量把定義和聲明分離開來

【82】【Cherno C++】【中字】C++的單例模式_嗶哩嗶哩_bilibili

Github代碼匯總

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

相關(guān)文章

  • C語(yǔ)言中的運(yùn)算符和結(jié)合性問題

    C語(yǔ)言中的運(yùn)算符和結(jié)合性問題

    這篇文章主要介紹了C語(yǔ)言中的運(yùn)算符和結(jié)合性問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Qt中圖片旋轉(zhuǎn)縮放操作的實(shí)現(xiàn)

    Qt中圖片旋轉(zhuǎn)縮放操作的實(shí)現(xiàn)

    本文主要介紹了Qt中圖片旋轉(zhuǎn)縮放操作的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-01-01
  • C語(yǔ)言中計(jì)算字符串長(zhǎng)度與分割字符串的方法

    C語(yǔ)言中計(jì)算字符串長(zhǎng)度與分割字符串的方法

    這篇文章主要介紹了C語(yǔ)言中計(jì)算字符串長(zhǎng)度與分割字符串的方法,是C語(yǔ)言入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-08-08
  • 利用C++如何覆蓋或刪除指定位置的文件內(nèi)容

    利用C++如何覆蓋或刪除指定位置的文件內(nèi)容

    這篇文章主要給大家介紹了關(guān)于利用C++如何覆蓋或刪除指定位置的文件內(nèi)容,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-08-08
  • C語(yǔ)言中輸入輸出流與緩沖區(qū)的深入講解

    C語(yǔ)言中輸入輸出流與緩沖區(qū)的深入講解

    一般情況下,由鍵盤輸入的字符并沒有直接送入程序,而是被存儲(chǔ)在一個(gè)緩沖區(qū)當(dāng)中。下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言中輸入輸出流與緩沖區(qū)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-09-09
  • OpenCV圖像處理基本操作詳解

    OpenCV圖像處理基本操作詳解

    這篇文章主要為大家詳細(xì)介紹了OpenCV圖像處理基本操作,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-03-03
  • C++ 11實(shí)現(xiàn)檢查是否存在特定的成員函數(shù)

    C++ 11實(shí)現(xiàn)檢查是否存在特定的成員函數(shù)

    C++11/14相比以往的C++98/03在很多方面做了簡(jiǎn)化和增強(qiáng),尤其是在泛型編程方面,讓C++的泛型編程的威力變得更加強(qiáng)大,下面這篇文章主要介紹了利用C++ 11實(shí)現(xiàn)檢查是否存在特定成員函數(shù)的相關(guān)資料,需要的朋友可以參考下。
    2017-02-02
  • Visual Studio 2019 Professional 激活方法詳解

    Visual Studio 2019 Professional 激活方法詳解

    這篇文章主要介紹了Visual Studio 2019 Professional 激活方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-05-05
  • C++判斷一個(gè)點(diǎn)是否在圓內(nèi)的方法

    C++判斷一個(gè)點(diǎn)是否在圓內(nèi)的方法

    這篇文章主要為大家詳細(xì)介紹了C++判斷一個(gè)點(diǎn)是否在圓內(nèi)的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • Visual Studio Code運(yùn)行程序時(shí)輸出中文成亂碼問題及解決方法

    Visual Studio Code運(yùn)行程序時(shí)輸出中文成亂碼問題及解決方法

    這篇文章主要介紹了解決Visual Studio Code運(yùn)行程序時(shí)輸出中文成亂碼問題,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-03-03

最新評(píng)論