從C++的RAII理解智能指針的思路(二)

Contact me:

Blog : cugtyt.github.io/blog/i

Email: cugtyt#qq.com, cugtyt#gmail.com


  • 從C++的RAII理解智能指針的思路(一)
  • 從C++的RAII理解智能指針的思路(二)

上次我們談到了unique_ptr的思路,那麼shared_ptr呢?注意到邏輯上這兩個是有差別的,unique_ptr只有一個指針管理內存,而share_ptr可以多個指針管理,那麼我們必須取消掉Unique中不能複製的限制,那麼多次釋放內存的問題怎麼解決呢?

答案是我們可以通過計數的方式,如果有新指針指向申請的內存,那麼計數值加1,少一個指針則減1,如果為0那麼我們就釋放內存,因此我們可以這樣做(注意到我們使用了指針存儲,這是為了保證所有的Shared使用同一個計數值):

class Shared {public: Shared(int n) { ptr = new int[n]; count = new unsigned; *count = 1; } Shared(const Shared& s) { count = s.count; ptr = s.ptr; ++(*count); } Shared& operator=(const Shared&) {/*與上面函數一樣*/} ~Shared() { --(*count); if (!*count) { delete[] ptr; delete count; } } // other funcprivate: unsigned *count; int *ptr;};int main() { Shared s1(10); Shared s2(s1);}

這樣一個最簡陋最粗糙的實現就完成了,再次提醒,這只是模型為了理解,和c++內部實現並不一樣,很多問題也沒有解決,只是為了粗略了解原理。

Shared有什麼問題呢,當我們創建Shared的時候,我們必須指定申請內存,也就是說指針和內存空間要建立聯繫,但是有的時候我們不希望此時建立聯繫,為什麼?也許我們只想需要的時候申請,不需要的時候就不用申請,這樣即使有指針,也可以釋放內存,或者只是做個檢查,看下這個內存是不是有人管理,別人可以釋放指針,這裡就充當輔助的角色。更重要的是,如果Shared出現循環引用,那麼內存還是會泄露,例如A->B,B->A,這樣二者的count永遠不會為0,也就內存泄露了,如果我們讓管理是weak的,也就是說指針雖然指向內存,但是不計數。此處沒有體現出這個問題,因為這是一個最為簡單的實現,我們可以設定初始時刻設置內存地址,這樣就可能出現這個問題了。

那怎麼實現呢?我上面的說明已經說的很清楚了,就是不增加計數,注意通常Weak要和Shared結合使用,這樣,我們需要做一下改動:

class Weak {public: Weak(Shared& s) { ptr = u.ptr; count = u.count; } Weak(const Weak& w) { count = w.count; ptr = w.ptr; } Weak& operator=(const Weak&) {/*與上面函數一樣*/} ~Weak() = default; bool alive() { /*檢查Shared是否存活*/ } // other funcprivate: unsigned *count; int *ptr;};int main() { Shared s1(10); Weak w1(s1); Weak w2(w1);}

我們使用Shared初始化Weak,然後保留Shared的信息(此處因為要訪問Shared的private成員,因此需要設置Weak為Shared的友元類),通過一個alive函數檢查對象是否存活,因為這只是個簡單的例子,因此alive的代碼沒有實現,如果想要了解如何實現,要看源碼細節,但是我們可以想像,因為可能要在Shared釋放後查看Shared的狀態,因此肯定還存在一塊區域記錄信息,此處過於細節我們略去。

好的,我們有了一個基本的認識,離實際的智能指針還有不小的距離,再次注意此處僅僅是個例子,細節需要看源碼,而且我們沒有考慮多線程情況下同步,Unique,Shared和Weak之間交互等問題,此處僅僅是希望對智能指針做了什麼做一個粗略的分析。


推薦閱讀:

TAG:C | 智能指針 | 內存管理 |