2018年6月14日 星期四

enable_shared_from_this 用途與使用範例

enable_shared_from_this 用途與使用範例

主要是用來處理 share_ptr 發生重複 delete 問題,下面用範例來說明問題
共享記憶體的本意,本意為
作為代理的物件可以任意複製,直到所有代理的物件被銷毀到最後一個才解構,代理所指向的物件的記憶體;即你可以複製或製作一堆共同指向地址p的指標,最後只解構一次p指向之物。
下面是正確的例子,有兩個指標 p 與 q 共同指向同一個地址,但只解構一次。
class Test {
    public:
    ~Test() {
        cout << this;
        cout << " Test Destructor." << endl;
    }
};

    shared_ptr<Test> p(new Test());
    shared_ptr<Test> q = p;
再來是出問題的例子,在嘗試讓函式返回自動指標的時候會出問題
class Test2: public enable_shared_from_this<Test2> {
    public:
    ~Test2() {
        cout << this;
        cout << " Test Destructor." << endl;
    }
    shared_ptr<Test2> getPoint() {
        return shared_ptr<Test2>(this);
};

// 錯誤1
    shared_ptr<Test2> p2(new Test2());
    shared_ptr<Test2> q2 = p2->getPoint();

// 錯誤2
    auto pp = new Test2();
    shared_ptr<Test2> p2(pp);
    shared_ptr<Test2> q2(pp);
這裡會對同一個地址執行兩次解構子,與共享記憶體的本意不符,至於為什麼會這樣可以從錯誤2來思考應該比較清晰,這裡兩個錯誤的原因是一樣的,只是錯誤1被包裝不容易看出,寫程序的時候可能會踩到坑。
不應該拿同一個指標來做兩個以上的自動指標,錯誤1的問題在於 return 的時候拿了 this 再做一次,這時候會沒有被記錄到程序會當作這是兩個不同的物件所以解構2次;正確的用法是一個指標只能產生一次自動指標,第二個以上的自動指標都要用自動指標產生/複製出自動指標,這樣物件就可以正確追蹤了。
錯誤2本來就不應該這樣寫直接拿 q2=p2 即可,錯誤1比較麻煩,環境所逼只能從成員函式拿指標的話那根本沒辦法避免(實際上為了保護,這種情況是很常見的手法)。
為此正確的處理方式就是使用 enable_shared_from_this<T> ,需要做兩個動作
  1. 繼承這個物件
  2. 返回 shared_from_this();
class Test2: public enable_shared_from_this<Test2> {
    public:
    ~Test2() {
        cout << this;
        cout << " Test Destructor." << endl;
    }
    shared_ptr<Test2> getPoint2() {
        return shared_from_this();
    }
};

    shared_ptr<Test2> p3(new Test2());
    shared_ptr<Test2> q3 = p3->getPoint2();
如此就能正常運作了。

參考

沒有留言:

張貼留言