Android智慧指標 (sp & wp)
http://bbs.chinabyte.com/thread-452223-1-1.html
在Android的原始碼中,經常會看到形如:sp<xxx>、wp<xxx>這樣的型別定義,這其實是Android中的智慧 指標。智慧指標是C++中的一個概念,通過基於引用計數的方法,解決物件的自動釋放的問題。在C++程式設計中,有兩個很讓人頭痛的問題:一是忘記釋放動態申
請的物件從而造成記憶體洩露;二是物件在一個地方釋放後,又在別的地方被使用,從而引起記憶體訪問錯誤。程式設計師往往需要花費很大精力進行精心設計,以避免這些 問題的出現。在使用智慧指標後,動態申請的記憶體將會被自動釋放(有點類似Java的垃圾回收),不需要再使用delete來釋放物件,也不需要考慮一個對 象是否已經在其它地方被釋放了,從而使程式編寫工作減輕不少,而程式的穩定性大大提高。 Android的智慧指標相關的原始碼在下面兩個檔案中:
frameworks/base/include/utils/RefBase.h
frameworks/base/libs/utils/RefBase.cpp
涉及的類以及類之間的關係如下圖所示:
Android中定義了兩種智慧指標型別,一種是強指標sp(strong pointer),一種是弱指標(weak pointer)。其實稱為強引用和弱引用更合適一些。強指標與一般意義的智慧指標概念相同,通過引用計數來記錄有多少使用者在使用一個物件,如果所有使 用者都放棄了對該物件的引用,則該物件將被自動銷燬。
弱指標也指向一個物件,但是弱指標僅僅記錄該物件的地址,不能通過弱指標來訪問該物件,也就是說不能通過弱智真來呼叫物件的成員函式或訪問物件的成員變 量。要想訪問弱指標所指向的物件,需首先將弱指標升級為強指標(通過wp類所提供的promote()方法)。弱指標所指向的物件是有可能在其它地方被銷 毀的,如果物件已經被銷燬,wp的promote()方法將返回空指標,這樣就能避免出現地址訪問錯的情況。
是不是很神奇?弱指標是怎麼做到這一點的呢?其實說穿了一點也不復雜,原因就在於每一個可以被智慧指標引用的物件都同時被附加了另外一個 weakref_impl型別的物件,這個物件中負責記錄物件的強指標引用計數和弱指標引用計數。這個物件是智慧指標的實現內部使用的,智慧指標的使用者 看不到這個物件。弱指標操作的就是這個物件,只有當強引用計數和弱引用計數都為0時,這個物件才會被銷燬。
說了這麼多原理,下面該看看到底智慧指標該怎麼使用了。假設現在有一個類MyClass,如果要使用智慧指標來引用這個類的物件,那麼這個類需滿足下列兩個前提條件:
(1) 這個類是基類RefBase的子類或間接子類;
(2) 這個類必須定義虛建構函式,即它的建構函式需要這樣定義:
virtual ~MyClass();
滿足了上述條件的類就可以定義智慧指標了,定義方法和普通指標類似。比如普通指標是這樣定義:
MyClass* p_obj;
智慧指標是這樣定義:
sp<MyClass> p_obj;
注意不要定義成 sp<MyClass>* p_obj。初學者容易犯這種錯誤,這樣實際上相當於定義了一個指標的指標。儘管在語法上沒有問題,但是最好永遠不要使用這樣的定義。
定義了一個智慧指標的變數,就可以象普通指標那樣使用它,包括賦值、訪問物件成員、作為函式的返回值、作為函式的引數等。比如:
p_obj = new MyClass(); // 注意不要寫成 p_obj = new sp<MyClass>
sp<MyClass> p_obj2 = p_obj;
p_obj->func();
p_obj = create_obj();
some_func(p_obj);
注意不要試圖delete一個智慧指標,即 delete p_obj。不要擔心物件的銷燬問題,智慧指標的最大作用就是自動銷燬不再使用的物件。不需要再使用一個物件後,直接將指標賦值為NULL即可:
p_obj = NULL;
上面說的都是強指標,弱指標的定義方法和強指標類似,但是不能通過弱指標來訪問物件的成員。下面是弱指標的示例:
wp<MyClass> wp_obj = new MyClass();
p_obj = wp_obj.promote(); // 升級為強指標。不過這裡要用.而不是->,真是有負其指標之名啊
wp_obj = NULL;
智慧指標用起來是很方便,在一般情況下最好使用智慧指標來代替普通指標。但是需要知道一個智慧指標其實是一個物件,而不是一個真正的指標,因此其執行效率是遠遠比不上普通指標的。所以在對執行效率敏感的地方,最好還是不要使用智慧指標為好。