溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C++?smart?pointer是什么及怎么用

發(fā)布時(shí)間:2022-08-25 10:18:49 來(lái)源:億速云 閱讀:123 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本篇內(nèi)容介紹了“C++ smart pointer是什么及怎么用”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

我們?yōu)槭裁葱枰猻mart pointer

眾所周知 新手寫(xiě)的c++代碼是很恐怖 壓根就不能用 其中最大的原因就在于新手寫(xiě)的代碼可能存在大量的內(nèi)存泄漏 那么為什么新手無(wú)法很好的去掌握內(nèi)存的東西呢 就是因?yàn)樵腸++并不像java那樣存在垃圾回收的機(jī)制 申請(qǐng)?jiān)诙褏^(qū)的資源都需要自己去回收 然而最痛苦的一件事情在于 指針的生命周期結(jié)束時(shí) 你會(huì)不小心就沒(méi)去回收他在堆區(qū)的資源 因?yàn)槎褏^(qū)資源的生命周期是很難把握的 有可能你析構(gòu)了 直接導(dǎo)致野指針訪問(wèn)異常那么為了解決這個(gè)問(wèn)題 c++就推出了智能指針 其中最重要的三種指針就是shared_ptr unique_ptr weak_ptr 接下來(lái)讓我們來(lái)講講如何將智能指針的生命周期和堆區(qū)資源的生命周期綁定起來(lái)吧

其實(shí)也非常簡(jiǎn)單 本質(zhì)就是當(dāng)這片堆區(qū)資源的引用計(jì)數(shù)變?yōu)?的時(shí)候就釋放這片內(nèi)存

smart pointer基本概念之引用計(jì)數(shù)

先來(lái)說(shuō)說(shuō)引用計(jì)數(shù) 這個(gè)東西是stl保證了肯定是線程安全的 所以即使你在多個(gè)線程內(nèi)同時(shí)去增加或者同時(shí)減少引用計(jì)數(shù)也并不會(huì)讓引用計(jì)數(shù)的值出現(xiàn)非你預(yù)期的結(jié)果

智能指針是和引用計(jì)數(shù)綁定在一起的 當(dāng)你創(chuàng)建智能指針指向一片資源時(shí) 引用計(jì)數(shù)就加一 當(dāng)智能指針析構(gòu)時(shí) 引用計(jì)數(shù)就減一 當(dāng)引用計(jì)數(shù)變?yōu)?時(shí) 堆區(qū)資源被析構(gòu)

smart pointer之shared_ptr

讓我們來(lái)看看下一段代碼

int main()
 {
	std::shared_ptr<std::string> i(new std::string("its good"));
	std::shared_ptr<std::string> j(new std::string("its bad"));
	std::vector<std::shared_ptr<std::string>> smartPointer_vec;
	for(int k=0;k<5;k++)
	smartPointer_vec.emplace_back(i);
	for (int k = 0; k < 4; k++)
	smartPointer_vec.emplace_back(j);
	for (auto &i : smartPointer_vec)
	{
		std::cout<<i->c_str();
		std::cout << i.use_count() << " ";
		std::cout << j.use_count() << std::endl;
		i = nullptr;
	}
	std::cout << i->c_str();
	std::cout << i.use_count() <<" ";
	std::cout << j.use_count() << std::endl;
}

聰明人看輸出 你就能完全明白 當(dāng)引用計(jì)數(shù)為0的時(shí)候就會(huì)析構(gòu) 其他不多說(shuō)了

C++?smart?pointer是什么及怎么用

重要講解:首先使用share_ptr去指向new出來(lái)的數(shù)據(jù)是性能低效的 最本質(zhì)的原因在于 他會(huì)進(jìn)行兩次內(nèi)存分配 第一次是對(duì)象堆區(qū)資源的申請(qǐng) 然后才是引用計(jì)數(shù)堆區(qū)資源的申請(qǐng) 而使用make_shared可以只進(jìn)行一次內(nèi)存分配 所以他更快 并且更安全 并且c++標(biāo)準(zhǔn)委員會(huì)也推薦你這么做 關(guān)于make_shared等下講解

自定義deleter(也就是自定義刪除器)

先說(shuō)我們?yōu)槭裁葱枰远x刪除器 因?yàn)樵谀承┣闆r下 我們希望當(dāng)智能指針指向的堆區(qū)資源釋放的時(shí)候進(jìn)行一些自定義操作也就是說(shuō)你可以玩一些很花的操作 但是也是那句話 stl并不會(huì)執(zhí)行任何安全檢查 崩了需要自己負(fù)責(zé)并且總所周知 new []這種形式的堆區(qū)資源需要我們使用delete[]來(lái)釋放 這就是最大的問(wèn)題 shared_ptr默認(rèn)是使用delete的 也就是說(shuō) 當(dāng)你使用shared_ptr去指向new []時(shí)如果不自定義刪除器 必然會(huì)造成內(nèi)存泄漏 如下圖所示的一段代碼就是經(jīng)典的內(nèi)存泄漏

C++?smart?pointer是什么及怎么用

正確的寫(xiě)法如下

C++?smart?pointer是什么及怎么用

即自定義一個(gè)刪除器 當(dāng)然你也可以玩一些移動(dòng)操作 也就是花哨的操作 當(dāng)然花哨操作就很多了 我只演示其中一種如下圖所示

C++?smart?pointer是什么及怎么用

運(yùn)行結(jié)果截圖如下:

C++?smart?pointer是什么及怎么用

Tips:當(dāng)你非常清楚你在干什么的時(shí)候再玩 功力不夠 不要亂玩

shared_ptr之make_shared

上文我們說(shuō)過(guò) 使用智能指針指向new出來(lái)的資源有一個(gè)問(wèn)題就是他會(huì)進(jìn)行兩次內(nèi)存分配 而標(biāo)準(zhǔn)委員會(huì)推薦創(chuàng)建shared_ptr的方式是使用make_shared 讓我們來(lái)看看make_shared是如何進(jìn)行堆區(qū)資源申請(qǐng)的 一個(gè)最簡(jiǎn)單的例子如下

int main()
{
	std::shared_ptr<int>p1(new int(5));
	//下面這種方式比上面這種方式性能更快 并且更加安全
	std::shared_ptr<int>p2 = make_shared<int>(5);
}

當(dāng)你使用make_shared的時(shí)候 又想去使用智能指針指向一個(gè)數(shù)組的時(shí)候 一個(gè)推薦的做法如下

int main()
{
	std::shared_ptr<std::vector<int>>p1(new std::vector<int>());
	//下面這種方式比上面這種方式性能更快 并且更加安全
	std::shared_ptr<std::vector<int>>p2 = make_shared<std::vector<int>>();
}

智能指針存在的問(wèn)題之循環(huán)引用

那么現(xiàn)在我們來(lái)看看shared_ptr存在的一些問(wèn)題 其中比較著名的一個(gè)問(wèn)題就是循環(huán)引用 什么叫循環(huán)引用呢 本人的觀點(diǎn)是當(dāng)你的智能指針指向的A堆區(qū)資源里又有智能指針去指向B堆區(qū)資源 而B(niǎo)堆區(qū)資源又存在一個(gè)智能指針來(lái)指向A堆區(qū)資源 而你能拿到的指針對(duì)半是全局或者是棧區(qū)的智能指針 你無(wú)法干預(yù)到堆區(qū)的智能指針的釋放 下面來(lái)看一個(gè)最簡(jiǎn)單的例子造成的循環(huán)引用 代碼如下圖所示

class SmartPointerTest
{
public:
	std::shared_ptr<SmartPointerTest> LoopRef{};
	int p[1000]{};
};
int main()
{
	std::shared_ptr<SmartPointerTest>p1(new SmartPointerTest());
	std::shared_ptr<SmartPointerTest>p2(new SmartPointerTest());
	p1->LoopRef = p2;
	p2->LoopRef = p1;
}

可以明顯看到 我們創(chuàng)建了兩個(gè)智能指針p1和p2 而p1指向的堆區(qū)資源里又有智能指針指向p2的堆區(qū)資源 同理p2 而當(dāng)main函數(shù)結(jié)束的時(shí)候 p1 p2指針被釋放 但是 這個(gè)時(shí)候 因?yàn)閮善褏^(qū)資源的引用計(jì)數(shù)都沒(méi)被置為0 所以不會(huì)釋放 那么這片堆區(qū)內(nèi)存也就永遠(yuǎn)的泄漏了 這是所有循環(huán)引用的原型 無(wú)論任何再?gòu)?fù)雜的循環(huán)引用都是建立在這個(gè)最基本的循環(huán)引用之上的

解決循環(huán)引用之weak_ptr

我們現(xiàn)在希望有一個(gè)方法來(lái)解決循環(huán)引用的問(wèn)題 并且我們也想去隨時(shí)拿到資源 那么我們?cè)撊绾巫瞿?標(biāo)準(zhǔn)委員會(huì)也考慮到了這個(gè)問(wèn)題 于是他提供了weak_ptr 當(dāng)他指向一片堆區(qū)資源的時(shí)候 并不會(huì)讓這片堆區(qū)資源的引用計(jì)數(shù)加一 而是作為這片資源的觀察者 當(dāng)需要這片資源的時(shí)候 隨時(shí)使用lock()函數(shù)來(lái)獲得一個(gè)shared_ptr來(lái)進(jìn)行使用 下面讓我們來(lái)看看如何使用weak_ptr 基于上面的例子

class SmartPointerTest
{
public:
	std::weak_ptr<SmartPointerTest> LoopRef{};
	int p[1000]{};
};
int main()
{
	std::shared_ptr<SmartPointerTest>p1(new SmartPointerTest());
	std::shared_ptr<SmartPointerTest>p2(new SmartPointerTest());
	p1->LoopRef = p2;
	p2->LoopRef = p1;
	//當(dāng)你想使用資源的時(shí)候 用下面的操作進(jìn)行
	std::cout << p1->LoopRef.lock()->p << std::endl;
}

輸出結(jié)果如下:

C++?smart?pointer是什么及怎么用

Tips:當(dāng)然weak_ptr的作用遠(yuǎn)遠(yuǎn)不止如此 他存在的意義僅僅是你想共享資源但是你并不想增加引用計(jì)數(shù) 解決循環(huán)引用只是順便解決的優(yōu)秀的程序員總是能知道在什么情況下使用何種指針來(lái)達(dá)到性能最優(yōu) lock()函數(shù) 顧名思義是要去給引用計(jì)數(shù)上鎖的 頻繁上鎖帶來(lái)的性能問(wèn)題不用多說(shuō)了吧

如果weak_ptr指向的資源已經(jīng)被析構(gòu) 那么他會(huì)拋出bad_weak_ptr的異常 請(qǐng)注意捕獲異常

智能指針問(wèn)題

無(wú)法創(chuàng)建指向自己的智能指針(本質(zhì)當(dāng)創(chuàng)建自己的智能指針時(shí)會(huì)創(chuàng)建兩個(gè)所屬組)

什么叫無(wú)法創(chuàng)建指向自己的智能指針呢 看如下這段代碼

class SmartPointerTest
{
public:
	std::weak_ptr<SmartPointerTest> LoopRef{};
	int p[1000]{};
	std::vector<std::shared_ptr<SmartPointerTest>> spt_vec;
	void MemberFuncTest()
	{
		spt_vec.push_back(std::shared_ptr<SmartPointerTest>(this));
	}
	int operator[](int i)
	{
		return p[i];
	}
};
int main()
{
	std::shared_ptr<SmartPointerTest>p1(new SmartPointerTest());
	p1->MemberFuncTest();
	std::cout<<p1.use_count()<<std::endl;
	system("pause");
}

我們預(yù)期的結(jié)果是把指向自己的智能指針傳入 并且引用計(jì)數(shù)為2 但是運(yùn)行結(jié)果如下:

C++?smart?pointer是什么及怎么用

并且程序會(huì)崩潰 為什么呢 因?yàn)槟阒貜?fù)釋放了 這就是我說(shuō)的 你會(huì)創(chuàng)建兩個(gè)組 而不是單純的增加引用計(jì)數(shù) 其本質(zhì)還是濫用普通指針和智能指針引起的麻煩

解決方法如下

代碼如下 我們可以繼承于std::enable_shared_from_this來(lái)解決

class SmartPointerTest :std::enable_shared_from_this<SmartPointerTest>
{
public:
	std::weak_ptr<SmartPointerTest> LoopRef{};
	int p[1000]{};
	std::vector<std::shared_ptr<SmartPointerTest>> spt_vec;
	void MemberFuncTest()
	{
		spt_vec.push_back(std::shared_ptr<SmartPointerTest>(shared_from_this()));
	}
	int operator[](int i)
	{
		return p[i];
	}
};
int main()
{
	std::shared_ptr<SmartPointerTest>p1(new SmartPointerTest());
	p1->MemberFuncTest();
	std::cout<<p1.use_count()<<std::endl;
	system("pause");
}

當(dāng)你這樣繼承自enable_shared_from_this的時(shí)候你就可以將自身的智能指針傳入而不是創(chuàng)建一個(gè)新的組避免了重復(fù)釋放非常的方便

“C++ smart pointer是什么及怎么用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI