溫馨提示×

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

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

關(guān)于c++ 智能指針及 循環(huán)引用的問(wèn)題

發(fā)布時(shí)間:2020-08-29 20:08:22 來(lái)源:腳本之家 閱讀:199 作者:jingxian 欄目:編程語(yǔ)言

c++智能指針介紹

由于 C++ 語(yǔ)言沒(méi)有自動(dòng)內(nèi)存回收機(jī)制,程序員每次 new 出來(lái)的內(nèi)存都要手動(dòng) delete,比如流程太復(fù)雜,最終導(dǎo)致沒(méi)有 delete,異常導(dǎo)致程序過(guò)早退出,沒(méi)有執(zhí)行 delete 的情況并不罕見(jiàn),并造成內(nèi)存泄露。如此c++引入 智能指針 ,智能指針即是C++ RAII的一種應(yīng)用,可用于動(dòng)態(tài)資源管理,資源即對(duì)象的管理策略。 智能指針在 <memory>標(biāo)頭文件的 std 命名空間中定義。 它們對(duì) RAII 或 獲取資源即初始化 編程慣用法至關(guān)重要。 RAII 的主要原則是為所有堆分配資源提供所有權(quán),例如動(dòng)態(tài)分配內(nèi)存或系統(tǒng)對(duì)象句柄、析構(gòu)函數(shù)包含要?jiǎng)h除或釋放資源的代碼的堆棧分配對(duì)象,以及任何相關(guān)清理代碼。

c++智能指針類別

c++ 智能指針主要包括:unique_ptr,shared_ptr, weak_ptr, 這三種,其中auto_ptr 已被遺棄。

unique_ptr

只允許基礎(chǔ)指針的一個(gè)所有者。 可以移到新所有者(具有移動(dòng)語(yǔ)義),但不會(huì)復(fù)制或共享(即我們無(wú)法得到指向同一個(gè)對(duì)象的兩個(gè)unique_ptr)。 替換已棄用的 auto_ptr。 相較于 boost::scoped_ptr。 unique_ptr 小巧高效;大小等同于一個(gè)指針,支持 rvalue 引用,從而可實(shí)現(xiàn)快速插入和對(duì) STL 集合的檢索。 頭文件:<memory>。

使用unique_ptr,可以實(shí)現(xiàn)以下功能:

1、為動(dòng)態(tài)申請(qǐng)的內(nèi)存提供異常安全。
2、將動(dòng)態(tài)申請(qǐng)內(nèi)存的所有權(quán)傳遞給某個(gè)函數(shù)。
3、從某個(gè)函數(shù)返回動(dòng)態(tài)申請(qǐng)內(nèi)存的所有權(quán)。
4、在容器中保存指針。
5、所有auto_ptr應(yīng)該具有的(但無(wú)法在C++ 03中實(shí)現(xiàn)的)功能。

如下代碼所示:

class A;
// 如果程序執(zhí)行過(guò)程中拋出了異常,unique_ptr就會(huì)釋放它所指向的對(duì)象
// 傳統(tǒng)的new 則不行
unique_ptr<A> fun1()
{
 unique_ptr p(new A);
 //do something
 return p;
}

void fun2()
{  // unique_ptr具有移動(dòng)語(yǔ)義
 unique_ptr<A> p = f();// 使用移動(dòng)構(gòu)造函數(shù)
 // do something
}// 在函數(shù)退出的時(shí)候,p以及它所指向的對(duì)象都被刪除釋放 shared_ptr 


采用引用計(jì)數(shù)的智能指針。 shared_ptr基于“引用計(jì)數(shù)”模型實(shí)現(xiàn),多個(gè)shared_ptr可指向同一個(gè)動(dòng)態(tài)對(duì)象,并維護(hù)了一個(gè)共享的引用計(jì)數(shù)器,記錄了引用同一對(duì)象的shared_ptr實(shí)例的數(shù)量。當(dāng)最后一個(gè)指向動(dòng)態(tài)對(duì)象的shared_ptr銷毀時(shí),會(huì)自動(dòng)銷毀其所指對(duì)象(通過(guò)delete操作符)。shared_ptr的默認(rèn)能力是管理動(dòng)態(tài)內(nèi)存,但支持自定義的Deleter以實(shí)現(xiàn)個(gè)性化的資源釋放動(dòng)作。頭文件:<memory>。

基本操作:shared_ptr的創(chuàng)建、拷貝、綁定對(duì)象的變更(reset)、shared_ptr的銷毀(手動(dòng)賦值為nullptr或離開(kāi)作用域)、指定deleter等操作。

shared_ptr的創(chuàng)建,有兩種方式,

一,使用函數(shù)make_shared(會(huì)根據(jù)傳遞的參數(shù)調(diào)用動(dòng)態(tài)對(duì)象的構(gòu)造函數(shù));

二,使用構(gòu)造函數(shù)(可從原生指針、unique_ptr、另一個(gè)shared_ptr創(chuàng)建)

shared_ptr<int> p1 = make_shared<int>(1);// 通過(guò)make_shared函數(shù)

shared_ptr<int> p2(new int(2));// 通過(guò)原生指針構(gòu)造此外智能指針若為“空“,即不指向任何對(duì)象,則為false,否則為true,可作為條件判斷??梢酝ㄟ^(guò)兩種方式指定deleter,一是構(gòu)造shared_ptr時(shí),二是使用reset方法時(shí)??梢灾剌d的operator->, operator *,以及其他輔助操作如unique()、use_count(), get()等成員方法。

weak_ptr

結(jié)合 shared_ptr 使用的特例智能指針。 weak_ptr 提供對(duì)一個(gè)或多個(gè) shared_ptr 實(shí)例所屬對(duì)象的訪問(wèn),但是,不參與引用計(jì)數(shù)。 如果您想要觀察對(duì)象但不需要其保持活動(dòng)狀態(tài),請(qǐng)使用該實(shí)例。 在某些情況下需要斷開(kāi) shared_ptr 實(shí)例間的循環(huán)引用。 頭文件:<memory>。

weak_ptr的用法如下:

weak_ptr用于配合shared_ptr使用,并不影響動(dòng)態(tài)對(duì)象的生命周期,即其存在與否并不影響對(duì)象的引用計(jì)數(shù)器。weak_ptr并沒(méi)有重載operator->和operator *操作符,因此不可直接通過(guò)weak_ptr使用對(duì)象。提供了expired()與lock()成員函數(shù),前者用于判斷weak_ptr指向的對(duì)象是否已被銷毀,后者返回其所指對(duì)象的shared_ptr智能指針(對(duì)象銷毀時(shí)返回”空“shared_ptr)。循環(huán)引用的場(chǎng)景:如二叉樹(shù)中父節(jié)點(diǎn)與子節(jié)點(diǎn)的循環(huán)引用,容器與元素之間的循環(huán)引用等。

智能指針的循環(huán)引用

循環(huán)引用問(wèn)題可以參考 這個(gè)鏈接 上的問(wèn)題理解,“循環(huán)引用”簡(jiǎn)單來(lái)說(shuō)就是:兩個(gè)對(duì)象互相使用一個(gè)shared_ptr成員變量指向?qū)Ψ降臅?huì)造成循環(huán)引用。導(dǎo)致引用計(jì)數(shù)失效。下面給段代碼來(lái)說(shuō)明循環(huán)引用:

#include <iostream>
#include <memory>
using namespace std;

class B;
class A
{
public:// 為了省去一些步驟這里 數(shù)據(jù)成員也聲明為public
 //weak_ptr<B> pb;
 shared_ptr<B> pb;
 void doSomthing()
 {
// if(pb.lock())
// {
//
// }
 }

 ~A()
 {
 cout << "kill A\n";
 }
};

class B
{
public:
 //weak_ptr<A> pa;
 shared_ptr<A> pa;
 ~B()
 {
 cout <<"kill B\n";
 }
};

int main(int argc, char** argv)
{
 shared_ptr<A> sa(new A());
 shared_ptr<B> sb(new B());
 if(sa && sb)
 {
 sa->pb=sb;
 sb->pa=sa;
 }
 cout<<"sa use count:"<<sa.use_count()<<endl;
 return 0;
}

上面的代碼運(yùn)行結(jié)果為:sa use count:2, 注意此時(shí)sa,sb都沒(méi)有釋放,產(chǎn)生了內(nèi)存泄露問(wèn)題?。?!

即A內(nèi)部有指向B,B內(nèi)部有指向A,這樣對(duì)于A,B必定是在A析構(gòu)后B才析構(gòu),對(duì)于B,A必定是在B析構(gòu)后才析構(gòu)A,這就是循環(huán)引用問(wèn)題,違反常規(guī),導(dǎo)致內(nèi)存泄露。

一般來(lái)講,解除這種循環(huán)引用有下面有三種可行的方法( 參考 ):

1 . 當(dāng)只剩下最后一個(gè)引用的時(shí)候需要手動(dòng)打破循環(huán)引用釋放對(duì)象。

2 . 當(dāng)A的生存期超過(guò)B的生存期的時(shí)候,B改為使用一個(gè)普通指針指向A。

3 . 使用弱引用的智能指針打破這種循環(huán)引用。

雖然這三種方法都可行,但方法1和方法2都需要程序員手動(dòng)控制,麻煩且容易出錯(cuò)。我們一般使用第三種方法:弱引用的智能指針weak_ptr。

強(qiáng)引用和弱引用

一個(gè)強(qiáng)引用當(dāng)被引用的對(duì)象活著的話,這個(gè)引用也存在(就是說(shuō),當(dāng)至少有一個(gè)強(qiáng)引用,那么這個(gè)對(duì)象就不能被釋放)。share_ptr就是強(qiáng)引用。相對(duì)而言,弱引用當(dāng)引用的對(duì)象活著的時(shí)候不一定存在。僅僅是當(dāng)它存在的時(shí)候的一個(gè)引用。弱引用并不修改該對(duì)象的引用計(jì)數(shù),這意味這弱引用它并不對(duì)對(duì)象的內(nèi)存進(jìn)行管理,在功能上類似于普通指針,然而一個(gè)比較大的區(qū)別是,弱引用能檢測(cè)到所管理的對(duì)象是否已經(jīng)被釋放,從而避免訪問(wèn)非法內(nèi)存。

使用weak_ptr來(lái)打破循環(huán)引用

代碼如下:

#include <iostream>
#include <memory>
using namespace std;

class B;
class A
{
public:// 為了省去一些步驟這里 數(shù)據(jù)成員也聲明為public
 weak_ptr<B> pb;
 //shared_ptr<B> pb;
 void doSomthing()
 {
 if(pb.lock())
 {

 }
 }

 ~A()
 {
 cout << "kill A\n";
 }
};

class B
{
public:
 //weak_ptr<A> pa;
 shared_ptr<A> pa;
 ~B()
 {
 cout <<"kill B\n";
 }
};

int main(int argc, char** argv)
{
 shared_ptr<A> sa(new A());
 shared_ptr<B> sb(new B());
 if(sa && sb)
 {
 sa->pb=sb;
 sb->pa=sa;
 }
 cout<<"sb use count:"<<sb.use_count()<<endl;
 return 0;
}

以上就是小編為大家?guī)?lái)的關(guān)于c++ 智能指針及 循環(huán)引用的問(wèn)題全部?jī)?nèi)容了,希望大家多多支持億速云~

向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