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

C++?smart?pointer全面深入講解

 更新時(shí)間:2022年08月25日 08:37:51   作者:殺神李  
一般在C/C++中,如果我們使用了pointer來指向某塊heap區(qū)域,當(dāng)不再需要這塊區(qū)域的時(shí)候,我們需要手動(dòng)刪除它。如果忘了的話,就會(huì)產(chǎn)生memory?leak

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

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

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

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

先來說說引用計(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

讓我們來看看下一段代碼

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) 其他不多說了

重要講解:首先使用share_ptr去指向new出來的數(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(也就是自定義刪除器)

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

正確的寫法如下

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

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

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

shared_ptr之make_shared

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

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>>();
}

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

那么現(xiàn)在我們來看看shared_ptr存在的一些問題 其中比較著名的一個(gè)問題就是循環(huán)引用 什么叫循環(huán)引用呢 本人的觀點(diǎn)是當(dāng)你的智能指針指向的A堆區(qū)資源里又有智能指針去指向B堆區(qū)資源 而B堆區(qū)資源又存在一個(gè)智能指針來指向A堆區(qū)資源 而你能拿到的指針對(duì)半是全局或者是棧區(qū)的智能指針 你無法干預(yù)到堆區(qū)的智能指針的釋放 下面來看一個(gè)最簡單的例子造成的循環(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ù)都沒被置為0 所以不會(huì)釋放 那么這片堆區(qū)內(nèi)存也就永遠(yuǎn)的泄漏了 這是所有循環(huán)引用的原型 無論任何再復(fù)雜的循環(huán)引用都是建立在這個(gè)最基本的循環(huán)引用之上的

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

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

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

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

智能指針問題

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

什么叫無法創(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é)果如下:

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

解決方法如下

代碼如下 我們可以繼承于std::enable_shared_from_this來解決

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ù)釋放非常的方便

關(guān)于unique_ptr我們將會(huì)在下一篇文章進(jìn)行詳細(xì)講解其實(shí)也很簡單就是他堆區(qū)資源的引用計(jì)數(shù)永遠(yuǎn)只可能是一也就是說他的資源只可能被一個(gè)指針指向附帶而來的有一些小細(xì)節(jié)和普通的shared_ptr不同我們也就留在下一章再說了

到此這篇關(guān)于C++ smart pointer全面深入講解的文章就介紹到這了,更多相關(guān)C++ smart pointer內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

最新評(píng)論