Smart pointer 是為了解決要了記憶體沒有還 – 導致 memory leak, 或是記憶體已經還了還在用 – 導致當機的問題. 直覺上, 還個記憶體好像並非難事, 何必大費周章呢? 主要是由函數返回指標時, 我們不能確定要在外面還還是裡面? 例如 [2] 所說的, 不是我 new 的, 需要我 delete 嗎?
DataGenerator DataGen;
int* A = DataGen.GetData();
delete A; // Should do this?
為了解決這個問題, 我們需要比較聰明的指標. 在 C++11 和 Android 裡面, smart pointer 的定義略有不同. 在 C++ 的話, 可以分為 3 種, 分別是: 前者的 unique ptr, shared ptr, waek ptr . 而 Android 裡面分為 strong pointer 和 weak pointer 兩種. 它們的定義分別如下 [1-2]. 如果是 MFC, QT… 的話, 都還有定義別種的 smart pointer [5-6], 基本上大同小異.
pointer |
定義 |
unique_ptr |
確保一份資源(被配置出來的記憶體空間)只會被一個 unique_ptr 物件管理的 smart pointer;當 unique_ptr 物件消失時,就會自動釋放資源。 |
shared_ptr |
可以有多個 shared_ptr 共用一份資源的 smart pointer,內部會記錄這份資源被使用的次數(reference counter),只要還有 shared_ptr 物件的存在、資源就不會釋放;只有當所有使用這份資源的 shared_ptr 物件都消失的時候,資源才會被自動釋放。 |
weak_ptr |
搭配 shared_ptr 使用的 smart pointer,和 shared_ptr 的不同點在於 weak_ptr 不會影響資源被使用的次數,也就是說的 weak_ptr 存在與否不代表資源會不會被釋放掉。 |
strong pointer |
等同於 C++ 11 的 shared pointer |
weak pointer |
弱指針只能指一個 object, 所以沒有 counter. 它只是提供一個地址, 甚至還不能調用 object 的 member function 或是 variable. |
強指針 (strong pointer = sp) 和一般的 shared_ptr 一樣, 透過 counter 來記錄有多少 “人" 引用這個 object?如果每個人都用完了, counter 歸零之後就可以被 free. [1,4] 而弱指針 (weak pointer = wp) 並不理會還有沒有人用, 只要自己用完就可以 delete 了. 當然, 只有在同時也沒有強指針指到它才會被 delete. 參考 [5] 的例子.
ref1, ref2, ref5 都是強指針 sp, ref3 是弱指針 wp. ref4 是 promote 過後的弱指針. 弱指針也不永遠是扶不起的阿斗, 它可以用 promote function 升級為強指針 (寫到這邊, 電視上正在演 “見龍卸甲", 劉德華救阿斗耶…).
// 先定義 class, Android 用的是 frameworksbaseincludeutilsRefBase.h
namespace android {
class RefTest : public RefBase {
public:
RefTest(int32_t id) : mID(id) {
printf(“RefTest ctor: %dn", mID);
}
virtual ~RefTest() {
printf(“RefTest dtor: %dn", mID);
}
int32_t id() const {
return mID;
}
private:
int32_t mID;
};
// 測試 strong pointer 的函數
int strong_pointer() {
sp<RefTest> ref1 = new RefTest(1);
sp<RefTest> ref5;
{
sp<RefTest> ref2 = new RefTest(2);
ref5 = ref2; // comment this out and check when the destructor of ref2 is called,
// ref5 又參考了 ref2
}
wp<RefTest> ref3 = ref1; // 弱指針也可以指向強指針, 但強指針不能指向弱指針.
sp<RefTest> ref4 = ref3.promote(); // 弱指針可以 promote 為強指針
if (ref4 == NULL) {
printf(“RefTest object is destroyedn“); // 強指針和弱指針都沒有人用, 就可以回收了. 但還不會發生.
} else {
printf(“RefTest object %d is still aroundn", // 此時還有人用
ref4->id());
}
ref4 = NULL; // ref4 清掉, ref3 是弱指針不用管, ref1 還在用
ref1 = NULL; // comment this out to check to check change in prints
// ref1 也不用了.
ref4 = ref3.promote(); // ref1 清掉, ref4 清掉, 所以 ref4 這次應該 NULL 了.
if (ref4 == NULL) {
printf(“RefTest object is destroyedn");
} else {
printf(“RefTest object %d is still aroundn",
ref4->id()); // 清掉了
}
printf(“Before return n");
return 0;
}
強指標的觀念帶有 counter, 似乎已經相當完備; 為什麼有強指針以後, 還要有弱指針? 甚至要那麼麻煩地去升級弱指針呢? 其實弱指針並不像它的名字那樣一無事處, 它也具有立刻把記憶體立即釋放掉的優勢. 在 “IOS5 ARC 完全手冊" [9] 的文件裡面, 它把弱指針的用途做了說明.
這次的 reference 特別多, 因此本來想簡單寫完, 不料化簡為繁, 又化繁為簡, 前後竟然跨了四天.
[ref]
1. Android智能指针使用方法介绍
2. 避免 memory leak:C++11 Smart Pointer
3. What are strong pointers and weak pointers
4. android smart pointer – [1] 的原文
5. Smart Pointers to boost your code
6. dhav’s blog
7. QT smart point 智能指针
8. C++内存管理
9. ios 5 arc完全指南