關(guān)于RAII,官方給出的解釋是這樣的“資源獲取就是初始化”。聽起來貌似不是很懂的哈,其實說的通俗點的話就是它是一種管理資源,避免內(nèi)存泄漏的一種方法。它可以保證在各種情況下,當(dāng)你對對象進(jìn)行使用時先通過構(gòu)造函數(shù)來進(jìn)行資源的分配和初始化,最后通過析構(gòu)函數(shù)來進(jìn)行清理,有效的保證了資源的正確分配和釋放。(特別是在異常中,因為異常往往會改變代碼正確的執(zhí)行順序,這就很容易引起資源管理的混亂和內(nèi)存的泄漏)
其中智能指針就是RAII的一種實現(xiàn)模式,所謂的智能就是它可以自動化的來管理它所指向那份空間的資源分配和釋放。下面先介紹一下庫中的智能指針吧:
這是Boost庫中的智能指針:
而在STL中之前是只有auto_ptr的,但在C++11標(biāo)準(zhǔn)中也引入了unique_ptr/shared_ptr/weak_ptr。(ps:unique_ptr就是Boost中的scoped_ptr)
接下來我就來好好的,仔細(xì)地介紹介紹它們哈:
1.auto_ptr(管理權(quán)的轉(zhuǎn)移)
很多人看書和資料上面說auto_ptr是一種變性類型的RAII,其實這里所說的變性實際上是一種管理權(quán)轉(zhuǎn)移特質(zhì),auto_ptr實際上就是通過這一特質(zhì)來實現(xiàn)資源的管理和釋放的,這就好比說一扇門只有一把鑰匙,拿鑰匙的人擁有開這扇門的權(quán)利,而當(dāng)另一個人從這個人這兒把鑰匙拿走后,他開門的權(quán)利也轉(zhuǎn)到另一個人那了,因為鑰匙被拿走了。
下面是一個簡單的auto_ptr的實現(xiàn),它能很好的證明上面的例子:
template<typename T> class AutoPtr { public: AutoPtr(T* ptr=NULL) :_ptr(ptr) {} AutoPtr(AutoPtr<T>& a) :_ptr(a._ptr) { a._ptr = NULL; } AutoPtr<T>& operator=(AutoPtr<T>& a) { _ptr = a._ptr; a._ptr = NULL; return *this; } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } ~AutoPtr() { if (_ptr != NULL) { delete _ptr; } } protected: T* _ptr; };
當(dāng)發(fā)生賦值運算和拷貝構(gòu)造時,之前的指針在賦值過后就被置成空了,也就是說真正能夠訪問內(nèi)存的只有當(dāng)前的指針。當(dāng)然這種方法也使它的局限性很高,因為之前的指針無法對再訪問該區(qū)域,這使得它的實用性并不強,之所以保留它主要還是為了維護之前的一些程序。
2.scoped_ptr(簡單粗暴的獨裁者)
首先我們先來看下它的簡單實現(xiàn)吧:
template<typename T> class ScopedPtr { public: ScopedPtr(T* ptr = NULL) :_ptr(ptr) {} ~ScopedPtr() { if (_ptr != NULL) { delete _ptr; } } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } protected: ScopedPtr(const ScopedPtr<T>& s); ScopedPtr<T>& operator=(const ScopedPtr<T>& s); protected: T* _ptr; };
其實從代碼中我們能很容易看出它的簡單粗暴了,它就根本不允許你對它進(jìn)行拷貝構(gòu)造和賦值,它將賦值重載和拷貝構(gòu)造兩個函數(shù)只進(jìn)行了聲明而沒有實現(xiàn),這樣它就強制限定你不可能在使用其他指針訪問這塊空間,所以說說它是個獨裁者一點也不為過,當(dāng)然這種指針一般是在特殊的場合出現(xiàn),并不常用,因為它限制了指針的一個很重要的特點:靈活性!
3.shared_ptr(計數(shù)器原理應(yīng)用)
shared_ptr是比較流行和實用的智能指針了,它通過計數(shù)器原理解決了上述兩種智能指針訪問唯一性的問題,它允許多個指針訪問同一塊空間,并且在析構(gòu)時也能夠保證內(nèi)存正確釋放。那它是怎樣一種機制呢?且看下面的代碼:
template<typename T> class SharedPtr { public: SharedPtr(T* ptr=NULL) :_ptr(ptr) , _pcount(new int(1)) {} SharedPtr(SharedPtr<T>& s) :_ptr(s._ptr) , _pcount(s._pcount) { ++(*_pcount); } SharedPtr<T>& operator=(SharedPtr<T> s) { swap(_ptr, s._ptr); swap(_pcount, s._pcount); return *this; } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } ~SharedPtr() { Reservs(); } public: void Reservs() { if (--(*_pcount) == 0) { delete _ptr; delete _pcount; } } protected: T* _ptr; int* _pcount; };
首先它在類模版中的成員變量增加了計數(shù)指針用來統(tǒng)計該內(nèi)存目前被多少指針管理,然后凡是有拷貝和賦值的統(tǒng)統(tǒng)在計數(shù)器上進(jìn)行累加,而在析構(gòu)的時候只需要檢查計數(shù)器內(nèi)當(dāng)前的計數(shù)是否唯1,不唯1的話說明當(dāng)前還有多個指針在使用它,那此時我們并不釋放它,只將它的計數(shù)減1就好;如果析構(gòu)時它的計數(shù)到1了,那就說明當(dāng)前只有一個指針在維護它,這時候再去釋放該內(nèi)存就變得很合理了。這就是shared_ptr整個實現(xiàn)過程和實現(xiàn)原理。
4.scoped_array和shared_ptr
關(guān)于scoped_array和shared_array,它們和scoped_ptr和shared_ptr其實大同小異,它們的實現(xiàn)原理都是一樣的,只不過一個是用new[]和delete[]的,一個是用new和delete的。本質(zhì)上他們是沒有任何區(qū)別的,通過下面的代碼我們能夠很直觀看出來:
scoped_array:
template<typename T> class ScopedArry { public: ScopedArry(T* ptr = NULL) :_ptr(ptr) {} ~ScopedArry() { if (_ptr != NULL) { delete[] _ptr; } } T& operator[](int index) { return _ptr[index]; } protected: ScopedArry(const ScopedPtr<T>& s); ScopedArry<T>& operator=(const ScopedArry<T>& s); protected: T* _ptr; };
shared_array:
template<typename T> class SharedArry { public: SharedArry(T* ptr = NULL) :_ptr(ptr) , _pcount(new int(1)) {} SharedArry(SharedArry<T>& s) :_ptr(s._ptr) , _pcount(s._pcount) { ++(*_pcount); } SharedArry<T>& operator=(SharedArry<T> s) { swap(_ptr, s._ptr); swap(_pcount, s._pcount); return *this; } T& operator[](int index) { return _ptr[index]; } ~SharedArry() { if (--(*_pcount) == 0) { delete[] _ptr; } } protected: T* _ptr; int* _pcount; };
整體而言數(shù)組我們只用重載[]就可以對其元素進(jìn)行訪問,并不用重載*和&來訪問它們了,這比指針相對而言能方便點。
5.weak_ptr(輔助shared_ptr)
上面介紹了shared_ptr,在這里要說明一點的是我上面的代碼并不是庫中的標(biāo)準(zhǔn)代碼,只是造了幾個輪子,這是為了方便向大家講解它們的實現(xiàn)原理和運行機制,其實真正庫里的代碼實現(xiàn)是很復(fù)雜的,下面我們可以看看boost庫中shared_ptr和weak_ptr的框架類圖:
其實通過這張圖我們可以看出智能指針的實現(xiàn)要比我們想象的復(fù)雜,但是它們實現(xiàn)的原理和我們介紹的是一樣一樣的,感興趣的同學(xué)可以去庫里面研究研究,博主就不一一的發(fā)出來了。
OK,我們再回到正題上來,為什么說weak_ptr是輔助shared_ptr的呢?其實在真正的運用中我們還會發(fā)現(xiàn)shared_ptr還有些不足之處,它有時并不能很好完成一些任務(wù),并且還會出現(xiàn)一些問題,其中和weak_ptr有關(guān)的一個問題就是——循環(huán)引用。
那循環(huán)引用是怎么造成的呢?請看下圖:
再來個代碼吧:
#include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> using namespace boost; struct ListNode { shared_ptr<ListNode > _prev; shared_ptr<ListNode > _next; //weak_ptr<ListNode > _prev; //weak_ptr<ListNode > _next; ~ ListNode() { cout<<"~ListNode()" <<endl; } }; void Test () { // 循環(huán)引用問題 shared_ptr <ListNode > p1( new ListNode ()); shared_ptr <ListNode > p2( new ListNode ()); cout <<"p1->Count:" << p1. use_count()<<endl ; cout <<"p2->Count:" << p2. use_count()<<endl ; // p1節(jié)點的_next指向 p2節(jié)點 p1->_next = p2; // p2節(jié)點的_prev指向 p1節(jié)點 p2->_prev = p1; cout <<"p1->Count:" << p1. use_count ()<<endl ; cout <<"p2->Count:" << p2. use_count ()<<endl ; }
當(dāng)我們用shared_ptr創(chuàng)建兩個雙向結(jié)點時,并將它們連接起來后就會出現(xiàn)問題,試想當(dāng)你用p1的_next指向p2時,它的引用計數(shù)會加1,同樣p2的_prev指向p1時也會使p1的引用計數(shù)增加,這就會出現(xiàn)一個問題——當(dāng)你釋放的時候,p2是要先釋放的,對吧?可是p2在釋放時并沒法將其指向的空間釋放掉,因為它的計數(shù)是2,它只會將計數(shù)器減1,而真正要釋放那塊空間的是p1_next,同樣當(dāng)p1進(jìn)行釋放時也只是計數(shù)器減1,它所指向的那塊空間也沒有被釋放,真正釋放那塊空間的其實是p2_prev,這時就導(dǎo)致了一個問題,就是兩邊都在等著對方先釋放,因此陷入無限的循環(huán)當(dāng)中。
這就是循環(huán)引用的出現(xiàn)的原因,從中我們可以清楚找到問題所在,就是在創(chuàng)建_next和_prev時使得其引用計數(shù)進(jìn)行了累加,因此為了解決此類問題我們引入了weak_ptr,它就是用來解決循環(huán)引用問題的,使用weak_ptr類型的指針并不會使shared_ptr的引用計數(shù)加1,這也就不會產(chǎn)生循環(huán)引用的問題了。下面可以通過上述代碼的運行結(jié)果直觀的看到weak_ptr實現(xiàn)機制:
使用shared_ptr:
使用weak_ptr:
這其實也是weak_ptr存在的意義,輔助shared_ptr,使得它們用起來跟我們使用平常的指針一模一樣,并且還非常方便,不用我們?nèi)タ紤]內(nèi)存的釋放和泄漏的問題。
好了,由于博主水平并不是很高,只能向大家解釋這么多了,有要補刀或有問題的大神請在下方留言哈。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
網(wǎng)站題目:淺談RAII&智能指針-創(chuàng)新互聯(lián)
文章鏈接:http://jinyejixie.com/article38/djchpp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站、網(wǎng)站設(shè)計公司、虛擬主機、全網(wǎng)營銷推廣、自適應(yīng)網(wǎng)站、網(wǎng)站設(shè)計
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容