關(guān)于c++ 智能指針及 循環(huán)引用的問題
c++智能指針介紹
由于 C++ 語言沒有自動內(nèi)存回收機(jī)制,程序員每次 new 出來的內(nèi)存都要手動 delete,比如流程太復(fù)雜,最終導(dǎo)致沒有 delete,異常導(dǎo)致程序過早退出,沒有執(zhí)行 delete 的情況并不罕見,并造成內(nèi)存泄露。如此c++引入 智能指針 ,智能指針即是C++ RAII的一種應(yīng)用,可用于動態(tài)資源管理,資源即對象的管理策略。 智能指針在 <memory>標(biāo)頭文件的 std 命名空間中定義。 它們對 RAII 或 獲取資源即初始化 編程慣用法至關(guān)重要。 RAII 的主要原則是為所有堆分配資源提供所有權(quán),例如動態(tài)分配內(nèi)存或系統(tǒng)對象句柄、析構(gòu)函數(shù)包含要刪除或釋放資源的代碼的堆棧分配對象,以及任何相關(guān)清理代碼。
c++智能指針類別
c++ 智能指針主要包括:unique_ptr,shared_ptr, weak_ptr, 這三種,其中auto_ptr 已被遺棄。
unique_ptr
只允許基礎(chǔ)指針的一個所有者。 可以移到新所有者(具有移動語義),但不會復(fù)制或共享(即我們無法得到指向同一個對象的兩個unique_ptr)。 替換已棄用的 auto_ptr。 相較于 boost::scoped_ptr。 unique_ptr 小巧高效;大小等同于一個指針,支持 rvalue 引用,從而可實現(xiàn)快速插入和對 STL 集合的檢索。 頭文件:<memory>。
使用unique_ptr,可以實現(xiàn)以下功能:
1、為動態(tài)申請的內(nèi)存提供異常安全。
2、將動態(tài)申請內(nèi)存的所有權(quán)傳遞給某個函數(shù)。
3、從某個函數(shù)返回動態(tài)申請內(nèi)存的所有權(quán)。
4、在容器中保存指針。
5、所有auto_ptr應(yīng)該具有的(但無法在C++ 03中實現(xiàn)的)功能。
如下代碼所示:
class A; // 如果程序執(zhí)行過程中拋出了異常,unique_ptr就會釋放它所指向的對象 // 傳統(tǒng)的new 則不行 unique_ptr<A> fun1() { unique_ptr p(new A); //do something return p; } void fun2() { // unique_ptr具有移動語義 unique_ptr<A> p = f();// 使用移動構(gòu)造函數(shù) // do something }// 在函數(shù)退出的時候,p以及它所指向的對象都被刪除釋放 shared_ptr
采用引用計數(shù)的智能指針。 shared_ptr基于“引用計數(shù)”模型實現(xiàn),多個shared_ptr可指向同一個動態(tài)對象,并維護(hù)了一個共享的引用計數(shù)器,記錄了引用同一對象的shared_ptr實例的數(shù)量。當(dāng)最后一個指向動態(tài)對象的shared_ptr銷毀時,會自動銷毀其所指對象(通過delete操作符)。shared_ptr的默認(rèn)能力是管理動態(tài)內(nèi)存,但支持自定義的Deleter以實現(xiàn)個性化的資源釋放動作。頭文件:<memory>。
基本操作:shared_ptr的創(chuàng)建、拷貝、綁定對象的變更(reset)、shared_ptr的銷毀(手動賦值為nullptr或離開作用域)、指定deleter等操作。
shared_ptr的創(chuàng)建,有兩種方式,
一,使用函數(shù)make_shared(會根據(jù)傳遞的參數(shù)調(diào)用動態(tài)對象的構(gòu)造函數(shù));
二,使用構(gòu)造函數(shù)(可從原生指針、unique_ptr、另一個shared_ptr創(chuàng)建)
shared_ptr<int> p1 = make_shared<int>(1);// 通過make_shared函數(shù)
shared_ptr<int> p2(new int(2));// 通過原生指針構(gòu)造此外智能指針若為“空“,即不指向任何對象,則為false,否則為true,可作為條件判斷。可以通過兩種方式指定deleter,一是構(gòu)造shared_ptr時,二是使用reset方法時??梢灾剌d的operator->, operator *,以及其他輔助操作如unique()、use_count(), get()等成員方法。
weak_ptr
結(jié)合 shared_ptr 使用的特例智能指針。 weak_ptr 提供對一個或多個 shared_ptr 實例所屬對象的訪問,但是,不參與引用計數(shù)。 如果您想要觀察對象但不需要其保持活動狀態(tài),請使用該實例。 在某些情況下需要斷開 shared_ptr 實例間的循環(huán)引用。 頭文件:<memory>。
weak_ptr的用法如下:
weak_ptr用于配合shared_ptr使用,并不影響動態(tài)對象的生命周期,即其存在與否并不影響對象的引用計數(shù)器。weak_ptr并沒有重載operator->和operator *操作符,因此不可直接通過weak_ptr使用對象。提供了expired()與lock()成員函數(shù),前者用于判斷weak_ptr指向的對象是否已被銷毀,后者返回其所指對象的shared_ptr智能指針(對象銷毀時返回”空“shared_ptr)。循環(huán)引用的場景:如二叉樹中父節(jié)點與子節(jié)點的循環(huán)引用,容器與元素之間的循環(huán)引用等。
智能指針的循環(huán)引用
循環(huán)引用問題可以參考 這個鏈接 上的問題理解,“循環(huán)引用”簡單來說就是:兩個對象互相使用一個shared_ptr成員變量指向?qū)Ψ降臅斐裳h(huán)引用。導(dǎo)致引用計數(shù)失效。下面給段代碼來說明循環(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; }
上面的代碼運行結(jié)果為:sa use count:2, 注意此時sa,sb都沒有釋放,產(chǎn)生了內(nèi)存泄露問題?。?!
即A內(nèi)部有指向B,B內(nèi)部有指向A,這樣對于A,B必定是在A析構(gòu)后B才析構(gòu),對于B,A必定是在B析構(gòu)后才析構(gòu)A,這就是循環(huán)引用問題,違反常規(guī),導(dǎo)致內(nèi)存泄露。
一般來講,解除這種循環(huán)引用有下面有三種可行的方法( 參考 ):
1 . 當(dāng)只剩下最后一個引用的時候需要手動打破循環(huán)引用釋放對象。
2 . 當(dāng)A的生存期超過B的生存期的時候,B改為使用一個普通指針指向A。
3 . 使用弱引用的智能指針打破這種循環(huán)引用。
雖然這三種方法都可行,但方法1和方法2都需要程序員手動控制,麻煩且容易出錯。我們一般使用第三種方法:弱引用的智能指針weak_ptr。
強(qiáng)引用和弱引用
一個強(qiáng)引用當(dāng)被引用的對象活著的話,這個引用也存在(就是說,當(dāng)至少有一個強(qiáng)引用,那么這個對象就不能被釋放)。share_ptr就是強(qiáng)引用。相對而言,弱引用當(dāng)引用的對象活著的時候不一定存在。僅僅是當(dāng)它存在的時候的一個引用。弱引用并不修改該對象的引用計數(shù),這意味這弱引用它并不對對象的內(nèi)存進(jìn)行管理,在功能上類似于普通指針,然而一個比較大的區(qū)別是,弱引用能檢測到所管理的對象是否已經(jīng)被釋放,從而避免訪問非法內(nèi)存。
使用weak_ptr來打破循環(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ī)淼年P(guān)于c++ 智能指針及 循環(huán)引用的問題全部內(nèi)容了,希望大家多多支持腳本之家~
相關(guān)文章
C++實現(xiàn)簡單的希爾排序Shell Sort實例
這篇文章主要介紹了C++實現(xiàn)簡單的希爾排序Shell Sort實例,對于正在學(xué)習(xí)算法的朋友很有借鑒價值,需要的朋友可以參考下2014-07-07