Smart pointer 小註解

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完全指南

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

%d 位部落客按了讚: