国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

C++知識點 -- 智能指針

這篇具有很好參考價值的文章主要介紹了C++知識點 -- 智能指針。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

C++知識點 – 智能指針


一、智能指針的使用及原理

1.使用場景

C++知識點 -- 智能指針
對于上面的場景,p1和p2在new申請空間后,div函數(shù)如果出現(xiàn)了除0錯誤,那么程序就會拋出異常,跳到接受異常的程序段繼續(xù)執(zhí)行,p1和p2申請的空間就沒有被正常釋放,造成了內(nèi)存泄漏;
這種場景我們就可以使用智能指針來解決空間的釋放問題。

2.RAII

RAII(Resource Acquisition Is Initialization)獲取到資源立即初始化,是一種利用對象生命周期來控制程序資源的技術(shù);
在對象構(gòu)造時獲取資源,接著控制對資源的訪問使其在對象的生命周期內(nèi)都有效,最后在對象析構(gòu)時釋放資源,我們實際上把管理一份資源的責(zé)任托管給了一個對象,好處在于:
(1)不需要顯式地釋放資源;
(2)采用這種方式,對象所需的資源在其生命周期內(nèi)始終有效;

3.智能指針的設(shè)計思想

(1)利用RAII的思想設(shè)計delete資源的類;
(2)像指針一樣的行為;

因此,智能指針實際上是一個對象,這個對象重載了operator->和operator*,具有像指針一樣的行為;

template<class T>
class SmartPtr
{
public:
	SmartPtr(T* ptr)
		: _ptr(ptr)
	{}

	~SmartPtr()
	{
		cout << "delete:" << _ptr << endl;
		delete _ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

private:
	T* _ptr;
};

上面的代碼就是一個簡易的智能指針,能夠?qū)崿F(xiàn)資源的釋放,以及像指針一樣的行為;

double Div(int a, int b)
{
	if (b == 0)
	{
		throw invalid_argument("除0錯誤");
	}
	return (double)a / b;
}

void test1()
{
	SmartPtr<int> sp1(new int);
	SmartPtr<int> sp2(new int);

	cout << Div(2, 0) << endl;

}

int main()
{
	try
	{
		test1();
	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}

	return 0;
}

使用智能指針對上面的代碼進(jìn)行重寫,讓智能指針托管開辟的新資源,當(dāng)sp1和sp2析構(gòu)時,其析構(gòu)函數(shù)會delete其管理的資源;
C++知識點 -- 智能指針

4.智能指針的拷貝問題

編譯器自動生成的拷貝構(gòu)造函數(shù)對于內(nèi)置類型會完成淺拷貝,這里的拷貝需要的就是淺拷貝,因為深拷貝會將管理的資源在其他地方拷貝一份,違背了功能需求;
但是淺拷貝又帶來了delete多次造成程序崩潰的問題,因此c++庫中設(shè)計了幾種智能指針來解決拷貝問題;

二、auto_ptr

這是c++98版本中提供的智能指針,auto_ptr對于拷貝的處理是管理權(quán)轉(zhuǎn)移;
下面是它的模擬實現(xiàn):

	template <class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			: _ptr(ptr)
		{}

		auto_ptr(auto_ptr<T>& ap)
			: _ptr(ap._ptr)
		{
			//管理權(quán)轉(zhuǎn)移
			ap._ptr = nullptr;
		}

		auto_ptr<T>& operator=(auto_ptr<T>& ap)
		{
			//檢測是否為自己給自己賦值
			if (this != &ap)
			{
				//釋放當(dāng)前對象中的資源
				if (_ptr)
				{
					delete _ptr;
				}

				//轉(zhuǎn)移ap中的資源到當(dāng)前對象
				_ptr = ap._ptr;
				ap._ptr = nullptr;
			}
			return *this;
		}

		~auto_ptr()
		{
			if (_ptr)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
			}
		}

		T* operator->()
		{
			return _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

	private:
		T* _ptr;
	};

auto_ptr對于拷貝的處理方式是管理權(quán)轉(zhuǎn)移,對于拷貝構(gòu)造,auto_ptr將資源的管理權(quán)轉(zhuǎn)移給新的對象,原來的對象的指針置空;
對于賦值重載,auto_ptr清空等號左邊的對象的資源,然后將等號右邊的對象管理的資源轉(zhuǎn)交給左邊的對象,右邊對象的指針置空;

auto_ptr對于智能指針拷貝的處理不是很好,所以很多項目都會禁止使用auto_ptr;

三、unique_ptr

c++11中開始提供unique_ptr,對拷貝的處理方式就是禁止拷貝;
下面是它的模擬實現(xiàn):

	template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			: _ptr(ptr)
		{}

		~unique_ptr()
		{
			if (_ptr)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
			}
		}

		T* operator->()
		{
			return _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		unique_ptr(const unique_ptr<T>& up) = delete;//只聲明不實現(xiàn)
		unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;

	private:
		T* _ptr;
	};

unique_ptr將拷貝構(gòu)造和賦值重載都進(jìn)行了只聲明不實現(xiàn)的操作,這樣類中就不會生成默認(rèn)的拷貝構(gòu)造和賦值重載了,也就禁止了unique_ptr對象間的拷貝;

四、shared_ptr

shared_ptr支持拷貝,原理是通過引用計數(shù)的方式來實現(xiàn)多個shared_ptr對象之間共享資源:
(1)每個資源都維護(hù)著一份計數(shù),用來記錄該份資源被幾個對象共享;
(2)在對象被銷毀時,對象引用計數(shù)減一;
(3)如果引用計數(shù)是0,就說明自己是最后一個被銷毀的對象,就必須釋放該資源;
(4)如果引用計數(shù)不是0,就不能釋放該資源;

1.模擬實現(xiàn)

如何實現(xiàn)多個對象共享一個引用計數(shù)呢?不能將引用計數(shù)設(shè)為static成員變量,因為這樣所有的資源對象都是用一個引用計數(shù);可以將引用計數(shù)的成員便變量設(shè)置成一個指針,只在對象構(gòu)造的時候new一個引用計數(shù),在拷貝和賦值的時候,只增加引用計數(shù);

	template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			: _ptr(ptr)
			, _pCount(new int(1)) // 只有在對象調(diào)用構(gòu)造函數(shù)的時候,才會新建一個管理資源的引用計數(shù)
		{}

		shared_ptr(const shared_ptr<T>& sp)
			: _ptr(sp._ptr)
			, _pCount(sp._pCount) // 拷貝構(gòu)造的時候不新建引用計數(shù),只拷貝資源指針和計數(shù)指針,并且計數(shù)++
		{
			*(_pCount)++;
		}

		T* operator->()
		{
			return _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}
		
		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			--(*_pCount); //指針指向新的資源,原來指向的資源計數(shù)需要--

			_ptr = sp._ptr;
			_pCount = sp._pCount;

			(*_pCount)++;

			return *this;
		}

	private:
		T* _ptr;

		//引用計數(shù)
		int* _pCount;
	};

上面代碼中的賦值重載是有問題的:
C++知識點 -- 智能指針
在這種情況下,sp1,2,3全部都賦值sp5,原先sp1,2,3指向的資源的引用計數(shù)就減為了0,但是上面的賦值重載函數(shù)并不能將原先的資源釋放掉,就造成了內(nèi)存泄露;
同時,還需要解決自己給自己賦值的問題(這里還要注意指向同一份資源之間的對象相互賦值);

		void Release()
		{
			if (--(*_pCount) == 0)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
				delete _pCount;
			}
		}

		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			if (_ptr == sp._ptr) //判斷指向的資源是不是同一塊資源
			{
				return *this;
			}

			Release();

			_ptr = sp._ptr;
			_pCount = sp._pCount;

			(*_pCount)++;

			return *this;
		}

整體模擬實現(xiàn)如下:

	template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			: _ptr(ptr)
			, _pCount(new int(1)) // 只有在對象調(diào)用構(gòu)造函數(shù)的時候,才會新建一個管理資源的引用計數(shù)
		{}

		shared_ptr(const shared_ptr<T>& sp)
			: _ptr(sp._ptr)
			, _pCount(sp._pCount) // 拷貝構(gòu)造的時候不新建引用計數(shù),只拷貝資源指針和計數(shù)指針,并且計數(shù)++
		{
			*(_pCount)++;
		}

		T* operator->()
		{
			return _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		void Release()
		{
			if (--(*_pCount) == 0)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
				delete _pCount;
			}
		}

		~shared_ptr()
		{
			Release();
		}
		
		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			if (_ptr == sp._ptr) //判斷指向的資源是不是同一塊資源
			{
				return *this;
			}

			Release();

			_ptr = sp._ptr;
			_pCount = sp._pCount;

			(*_pCount)++;

			return *this;
		}

		int use_count()
		{
			return *_pCount;
		}

		T* get()
		{
			return _ptr;
		}

	private:
		T* _ptr;

		//引用計數(shù)
		int* _pCount;
	};

2.shared_ptr的循環(huán)引用

C++知識點 -- 智能指針
如上圖所示,這種情況下,左右兩個節(jié)點都由shared_ptr n1、n2管理,節(jié)點中的指針同樣也是shared_ptr,左邊節(jié)點的next指向右邊節(jié)點,右邊節(jié)點的prev指向左邊節(jié)點,也就是next管著右邊節(jié)點的內(nèi)存塊,prev管著左邊節(jié)點的內(nèi)存塊,那么左右兩個節(jié)點的引用計數(shù)都是2;
如果僅僅析構(gòu)n1和n2,那么左右兩節(jié)點的引用計數(shù)都會減為1,不會觸發(fā)資源的回收,因為還有next和prev在指向著資源;只有在左邊節(jié)點被釋放,調(diào)用了析構(gòu)函數(shù)時,next才會被釋放,右邊節(jié)點的引用計數(shù)才能減到0,才能夠釋放,顯然無法直接釋放左邊的節(jié)點;

為了解決shared_ptr的循環(huán)引用問題,c++11引入了weak_ptr

五、weak_ptr

weak_ptr不是常規(guī)的智能指針,沒有RAII,不直接管理資源,weak_ptr主要用shared_ptr構(gòu)造,用來解決shared_ptr的循環(huán)引用問題;
C++知識點 -- 智能指針
weak_ptr訪問資源時,不增加引用計數(shù),將節(jié)點成員的指針next和prev設(shè)置成weak_ptr就不存在循環(huán)引用的問題了;

模擬實現(xiàn)

	template<class T>
	class weak_ptr
	{
	public:
		weak_ptr()
			: _ptr(nullptr)
		{}

		weak_ptr(const shared_ptr<T>& sp)
			: _ptr(sp.get())
		{}

		weak_ptr(const weak_ptr<T>& wp)
			: _ptr(wp._ptr)
		{}

		weak_ptr<T>& operator=(const weak_ptr<T>& wp)
		{
			_ptr = wp._ptr;
			return *this;
		}

		T* operator->()
		{
			return _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

	private:
		T* _ptr;
	};

六、定制刪除器

如果自定義類型使用new [ ]申請空間,使用delete釋放可能會出錯;
C++知識點 -- 智能指針
delete[]是在開空間時多開4byte的空間在頭部,儲存new的對象的個數(shù),在delete[]的時候就知道要調(diào)用多少次析構(gòu)函數(shù)了;
shared_ptr和unique_ptr都支持定制刪除器

C++知識點 -- 智能指針
shared_ptr需要在調(diào)用構(gòu)造函數(shù)初始化時傳一個仿函數(shù)對象或者lambda表達(dá)式;
unique_ptr需要傳模板參數(shù)為仿函數(shù);

template<class T>
struct DeleteArray
{
	void operator()(T* ptr)
	{
		cout << "delete:" << ptr << endl;
		delete[] ptr;
	}
};

template<class T>
struct Free
{
	void operator()(T* ptr)
	{
		cout << "delete:" << ptr << endl;
		free(ptr);
	}
};

int main()
{
	//調(diào)用構(gòu)造函數(shù)時傳仿函數(shù)對象
	std::shared_ptr<int> n1(new int[5], DeleteArray<int>());
	std::shared_ptr<int> n2((int*)malloc(5 * sizeof(int)), Free<int>());

	//調(diào)用構(gòu)造函數(shù)時傳lambda表達(dá)式
	std::shared_ptr<int> n3(new int[5], [](int* ptr) {delete[] ptr; });

	//unique_ptr在模板參數(shù)中傳仿函數(shù)
	std::unique_ptr<int, DeleteArray<int>> n4(new int[5]);

	return 0;
}

七、內(nèi)存泄漏

1.什么是內(nèi)存泄漏

內(nèi)存泄漏是指因為疏忽或錯誤導(dǎo)致程序未能釋放已經(jīng)不再使用的內(nèi)存的情況;內(nèi)存泄漏不是內(nèi)存在物理上消失,而是因為設(shè)計錯誤而失去了對內(nèi)存的控制(指針丟失);
如果內(nèi)存還在,進(jìn)程正常結(jié)束,內(nèi)存也會釋放;

2.內(nèi)存泄露的危害

長期內(nèi)存泄漏,將導(dǎo)致程序相應(yīng)越來越慢,直到卡死;

3.如何避免內(nèi)存泄漏

(1)養(yǎng)成良好的工程編碼規(guī)范,申請的內(nèi)存記著去釋放;
(2)使用RAII的思想或者智能指針來管理資源;
(3)使用內(nèi)存泄漏檢測工具;
文章來源地址http://www.zghlxwxcb.cn/news/detail-437503.html

到了這里,關(guān)于C++知識點 -- 智能指針的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 【C語言】讓你不再害怕“指針”【知識點詳解】

    【C語言】讓你不再害怕“指針”【知識點詳解】

    目錄 一.什么是指針為什么需要指針? 1.什么是指針? 2.為什么需要指針? 以一個代碼為例觀察地址:(這里我們可以通過調(diào)試和打印兩種方式觀察) 1.調(diào)試觀察: 2.打印觀察(地址是用%p 打?。?二.地址編號的來源指針變量的大小 產(chǎn)生來源 指針變量大小 三.指針類型??

    2024年02月08日
    瀏覽(26)
  • 人工智能領(lǐng)域的關(guān)鍵知識點

    ? ? 非常抱歉,由于人工智能涉及的領(lǐng)域非常廣泛,50個關(guān)鍵知識點無法詳細(xì)覆蓋所有領(lǐng)域。不過,以下是人工智能領(lǐng)域中的一些關(guān)鍵知識點: 1. 機(jī)器學(xué)習(xí):機(jī)器學(xué)習(xí)是一種讓計算機(jī)系統(tǒng)通過學(xué)習(xí)數(shù)據(jù)而不是硬編碼程序來改善性能的方法。 2. 深度學(xué)習(xí):深度學(xué)習(xí)是機(jī)器學(xué)習(xí)的

    2024年02月08日
    瀏覽(28)
  • C++碎知識點

    C++碎知識點

    二叉樹 由 n個節(jié)點構(gòu)成的形態(tài)不同的?叉樹 同余符號 定義設(shè)m是大于1的正整數(shù),a,b是整數(shù),如果m|(a-b),則稱a與b關(guān)于模m同余,記作abmod(m),讀作a同余于b模m。 符號= 按位與 后賦值 C語言中計算優(yōu)先級 1LL 1LL會在運算時把后面的臨時數(shù)據(jù)擴(kuò)容成long?long類型,再在賦值給左邊時轉(zhuǎn)

    2024年02月12日
    瀏覽(19)
  • 人工智能期末復(fù)習(xí)——速通知識點

    人工智能期末復(fù)習(xí)——速通知識點

    知識點是通過老師上課ppt整理,對于期末復(fù)習(xí)的基本考點都有涉及,以及計算題部分都有例題進(jìn)行講解,希望能幫助大家更好的復(fù)習(xí)。 智能的主要流派: 思維理論:智能的核心是思維 知識閾值理論:智能取決于知識的數(shù)量及一般化程度 進(jìn)化理論:用控制取代知識的表示 智

    2024年02月03日
    瀏覽(25)
  • 論文查重的小知識點 智能寫作

    論文查重的小知識點 智能寫作

    大家好,今天來聊聊論文查重的小知識點 智能寫作,希望能給大家提供一點參考。 以下是針對論文重復(fù)率高的情況,提供一些修改建議和技巧,可以借助此類工具: 論文查重的小知識點 一、背景介紹 在學(xué)術(shù)領(lǐng)域,論文查重是保證學(xué)術(shù)誠信的重要手段。隨著學(xué)術(shù)研究的日益

    2024年01月21日
    瀏覽(23)
  • 力扣刷題(C++)知識點

    力扣刷題(C++)知識點

    一,找到數(shù)組的中間位置 這個是錯的,+=不能分開來 ? C++ vectorint nums 用法 創(chuàng)建一維數(shù)組vector: vectorint nums;? //不指定長度 vectorint nums(n);? ? //指定長度為n ? c++ <numeric> accumulate 函數(shù) accumulate函數(shù)實現(xiàn)將一段數(shù)字從頭到尾累加起來 前兩個參數(shù)是指定累加的范圍,第三個參數(shù)

    2024年02月13日
    瀏覽(19)
  • 波奇學(xué)C++:多態(tài)知識點

    波奇學(xué)C++:多態(tài)知識點

    結(jié)果是 student 0 原因在于重寫時只重寫函數(shù)的實現(xiàn),就是說相當(dāng)于Person的fun的聲明和Student的函數(shù)實現(xiàn)的拼在一起所以缺省值是0。 如果是子類指針或者引用就不是多態(tài)調(diào)用了只是單存子類對父類的重定義,隱藏函數(shù)。 上一篇文章提到的,多態(tài)的本質(zhì)就是基類和派生類的虛表中

    2024年02月09日
    瀏覽(22)
  • 一些關(guān)于c++的瑣碎知識點

    一些關(guān)于c++的瑣碎知識點

    目錄 bool強(qiáng)轉(zhuǎn) const構(gòu)成重載:const修飾*p ?移動構(gòu)造 new int (10)所做的四件事 this指針---為什么函數(shù)里面需要this指針? .和-的區(qū)別 new創(chuàng)建對象 仿函數(shù) new和malloc的區(qū)別 c++系統(tǒng)自動給出的函數(shù)有 delete和delete[ ]區(qū)別何在 檢查有沒有析構(gòu)函數(shù) explict外部 內(nèi)存泄漏的本質(zhì):丟失了內(nèi)存地

    2024年02月07日
    瀏覽(21)
  • C++基礎(chǔ)知識點整理筆記(四)

    10. C++的內(nèi)存管理 在C++中,內(nèi)存被分成五個區(qū):棧、堆、自由存儲區(qū)、靜態(tài)存儲區(qū)、常量區(qū) (一) 棧:存放函數(shù)的參數(shù)和局部變量,編譯器自動分配和釋放 (二) 堆:new動態(tài)分配的內(nèi)存,由程序員手動進(jìn)行釋放,否則程序結(jié)束后,由操作系統(tǒng)自動進(jìn)行回收 (三) 自由存儲區(qū)

    2024年02月15日
    瀏覽(31)
  • C++筆記(細(xì)碎小知識點)1

    2024年02月08日
    瀏覽(23)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包