http://blog.csdn.net/dawn_sf/article/details/70168930
智能指針
____________________________________________________
今天我們來看一個高大上的東西,它叫智能指針。 哇這個名字聽起來都智能的不得了,其實等你了解它你一定會有一點失望的。。。。因為它說白了
就是個管理資源的。智能指針的原理就是管理資源的RALL機制,我們先來簡單了解一下
RALL機制:RALL機制便是通過利用對象的自動銷毀,使得資源也具有了生命周期,有了自動銷毀(自動回收)的功能。RAII全稱為Resource?
Acquisition Is Initialization,它是在一些面向對象語言中的一種慣用法。RAII源于C++,在Java,C#,D,Ada,Vala和Rust中也有應用。資源分配
即初始化,定義一個類來封裝資源的分配和釋放,在構造函數完成資源的分配和初始化,在析構函數完成資源的清理,可以保證資源的正確初始化和釋
放。RAII要求,資源的有效期與持有資源的對象的生命期嚴格綁定,即由對象的構造函數完成資源的分配(獲取),同時由析構函數完成資源的釋放。在
這種要求下,只要對象能正確地析構,就不會出現資源泄露問題。RALL在這里就是簡單提一下而已,現在我們來看我們今天的主角智能指針。
?
智能指針(smart pointer)是存儲指向動態分配(堆)對象指針的類。它的誕生理由就是,為粗心和懶的人設計的,但是這個設計一定不是反人類
的,因為無論你有多厲害只要你是人你總會有犯錯誤的時候,所以智能指針可以很好地幫助我們,程序員每次?new?出來的內存都要手動?delete。程序
員忘記?delete,流程太復雜,最終導致沒有?delete,
異常導致程序過早退出,沒有執行?delete?的情況并不罕見。其實智能指針只是怕你忘了delete,而專門設置出來的一個對象。有沒有感覺它頓時不夠
智能呢,但是你絕對不能否認它的實用性和重要性。現在我們來看看智能指針的使用吧:
對于編譯器來說,智能指針實際上是一個棧對象,并非指針類型,在棧對象生命期即將結束時,智能指針通過析構函數釋放有它管理的堆內存。所有智
能指針都重載了“operator->”操作符,直接返回對象的引用,用以操作對象。訪問智能指針原來的方法則使用“.”操作符。先拋開智能指針的幾個
版本不說,我們先來講一下它里面的 * 和 -> 是如何進行運算符重載的。下面是我定義的一個類,他只是為了實現原生指針的 * 和 -> 功能:
請忽略這個粗糙的A類和AA結構體,我們的目的只是實現原生函數的功能,那么我的功能實現了嗎?
? ? ? ? ? ?
這里結果沒有一點問題,那么我們現在的注意點就應該放在這里是如何實現的:
智能指針的三大版本的實現==>
好了前面那些磨人的小妖精終于清理完了,現在我們真真正正的進入主題,智能指針的發展史以及它的常見的三個版本。
? ? ? ? ? ? ? ? ? ? ? ?1.管理權轉移 ? 2.簡單粗暴的防拷貝 ?3.引用計數版本
注意這里我只是實現簡單的思想,可能寫的不是很好,望大家指出幫助我改正錯誤。
管理權轉移==>
這個智能指針是1998應用到VS上的,現在我們來實現第一個,何為管理權轉移呢?
現在我列出該思想的實現代碼:
現在我們先看看它使用普通操作時的結果如何:
現在的結果真的太符合我們的預料了,我們要的就是這樣的結果,當你還沉浸自己成功的喜悅的時候,這里雖然成功實現了自動釋放空間的功能還有指
針的功能,但是看看下面這種情況:我們把main函數內修改成這個樣子:
int main()
?{
AutoPtr<int>ap1(new int);
*ap1 = 10;
AutoPtr<int>ap2(ap1);
cout << *ap1 << endl;
return 0;
?}
?{
AutoPtr<int>ap1(new int);
*ap1 = 10;
AutoPtr<int>ap2(ap1);
cout << *ap1 << endl;
return 0;
?}
然后結果。。調試到這一步程序崩潰了,罪魁禍首就是AutoPtr<int>ap2(ap1),這里原因就是ap2完全的奪取了ap1的管理權。然后導致ap1無家可歸,
訪問它的時候程序就會崩潰。如果在這里調用ap2 = ap1程序一樣會崩潰原因還是ap1被徹徹底底的奪走一切,所以這種編程思想及其不符合C++思想,
所以它的設計思想就是有一定的缺陷。所以一般不推薦使用Autoptr智能指針。 使用了也絕對不能使用"="和拷貝構造。歷史在發展,所以我們見到接
下來這種想法:??
簡單粗暴法(防拷貝)==>
scoped智能指針?屬于?boost?庫,定義在?namespace boost?中,包含頭文件#include<boost/smart_ptr.hpp>?便可以使用。scoped智能指
針?跟?AutoPtr智能指針?一樣,可以方便的管理單個堆內存對象,特別的是,scoped智能指針?獨享所有權,避免了?AutoPtr智能指針惱人的幾個問
題,它直接就告訴用戶我不提供"="和拷貝構造這兩個功能,你別用,用了我也讓你編不過去。來看它的實現:
它的意思就是,我根本不會提供拷貝構造 和 "="的功能,他強任他強,我就是這樣。他確實解決上一個智能指針的問題,他直接讓用戶不能使用這個
功能,這個思想確實有點反人類。。由于scoped智能指針獨享所有權,當我們真真需要復制智能指針時,需求便滿足不了了,如此我們再引入一個智能
指針,專門用于處理復制,參數傳遞的情況。這便是如下的shared智能指針。
引用計數版本==>
接下來我們看最后一種,也就是我們現在經常用到的shared智能指針,等到智能指針發展到這一步也就很成熟了它已經幾乎完美的解決所有功能,因為
它使用了引用計數版本當指向該片資源的*_num變成0的時候,釋放該資源.
上面就是我實現的簡易的shared智能指針,現在我們調用這個智能指針,我們來看看結果:
我們發現它完美的解決了一切功能,這個指針真的算是很完美的思想,不過你再完美也會有瑕疵,要不然也不會有boost::weak_ptr的存在,
boost::weak_ptr的存在就是為boost::shared_ptr解決一點點瑕疵的。這個問題藏得極深一般不會遇到的,但是當你真的遇到的時候,我相信你會
絞盡腦汁的找BUG,還是很難找的。話不多說,現在我們來看下面這個例子:
現在我們驗證shared智能指針的缺陷,就不用我實現的那個了,那個好多功能我都沒實現,我們用專家寫的shared_ptr智能指針,構造兩個雙向鏈表里
面的結點,這里這個雙向鏈表可能有一點簡陋,但是我們只是需要它的prev和next指針就夠了。現在我們運行代碼看看會發生什么情況:
現在cur和next指針所管理的結點現在都有兩個指針指針管理,然后在這里會發生這樣一件事:
循環引用一般都會發生在這種"你中有我,我中有你"的情況里面,這里導致的問題就是內存泄漏,這段空間一直都沒有釋放,現在很明顯引用計數在這
里就不是很合適了,但是shared_ptr除了這里不夠完善,其他的地方都是非常有用的東西,所以編寫者在這里補充一個week_ptr,接下來我們看最后一
個智能指針week_ptr。
week_ptr==>
weak_ptr是為了配合shared_ptr而引入的一種智能指針,它更像是shared_ptr的一個助手而不是智能指針,因為它不具有普通指針的行為,沒有重載
operator*和->,它的最大作用在于協助shared_ptr工作,像旁觀者那樣觀測資源的使用情況.通俗一點講就是,首先weak_ptr?是專門為shared_ptr?而
準備的。現在我們并不能根據內部的引用計數。weak_ptr?是?boost::shared_ptr?的觀察者對象,觀察者意味著weak_ptr?只對shared_ptr 進行引用而
不改變其引用計數,當被觀察的shared_ptr?失效后,相應的weak_ptr?也相應失效,然后它就什么都不管光是個刪 , 也就是這里的cur和next在析
構的時候 , 不用引用計數減一 , 直接刪除結點就好。這樣也就間接地解決了循環引用的問題,當然week_ptr指針的功能不是只有這一個。但是現在
我們只要知道它可以解決循環引用就好。
現在總結一下:
1、在可以使用?boost?庫的場合下,拒絕使用?std::auto_ptr,因為其不僅不符合?C++?編程思想。
2、在確定對象無需共享的情況下,使用?boost::scoped_ptr。
3、在對象需要共享的情況下,使用?boost::shared_ptr。
4、在需要訪問?boost::shared_ptr?對象,而又不想改變其引用計數的情況下(循環引用)使用boost::weak_ptr。
5、最后一點,在你的代碼中,盡量不要出現?delete?關鍵字,因為我們有智能指針。