2011-11-09 28 views
6

兩相結構採用以下形狀:智能指針是否需要兩階段構造?

struct something { 
    something() 
     : p1(NULL) 
     , p2(NULL) 
    { } 

    ~something() { 
     if (p1) delete p1; 
     if (p2) delete p2; 
    } 

    void initialize() { 
     p1 = new int(2); 
     p2 = new int(5); // May throw if allocation fails! 
    } 

    int* p1; 
    int* p2; 
}; 

的點,這是一個天真的構造(即不看用於分配失敗)將泄漏存儲器:的局部構造的對象的析構函數是從來沒有所謂。

我的問題:下面的代碼是否安全,並且通過確認,智能指針是否消除了兩階段構造?

struct something { 
    something() 
     : p1(new int(2)) 
     , p2(new int(5)) 
    { } 

    std::unique_ptr<int> p1; 
    std::unique_ptr<int> p2; 
}; 
+6

在'delete'之前不需要條件。嚴重的是,你沒有。刪除null非常好。 –

+0

還有一些非拋類型的新的和刪除的內存分配,就像malloc()/ free()一樣。但即使分配本身沒有使用非拋出new/delete失敗,對象構造函數仍然可能拋出。 –

+0

@KerrekSB注意。 :) –

回答

5

是的,你的新代碼很好。但是請注意,有可能在微妙更復雜的情況:

#include <memory> 

struct foo { 
    foo(std::shared_ptr<int> a, std::shared_ptr<int> b) { } 
}; 

struct bar { 
    foo f; 
    bar() : f(std::shared_ptr<int>(new int), std::shared_ptr<int>(new int)) { } 
}; 

int main() { 
    bar b; 
} 

不會是安全然而,由於的foobar初始化列表構造函數的參數計算順序是不確定的。符合標準的編譯器可能會選擇進行深度或廣度的第一級評估(或者只要它們全部都得到正確評估)。這意味着如果第一個new int成功,但第二個在shared_ptr構建對象之前拋出,則要執行的第一個分配仍可能泄漏。

如果你發現自己想要做到這一點,除了迴歸到兩階段結構之外,還有兩種可能的解決方案:第一種可能是重構,第二種可能是先構建單獨的shared_ptr作爲條形的成員,之前f。哪一個最合適是我認爲需要根據具體情況進行的判斷呼叫。

5

我的問題:下面的代碼安全,

是的,這將是確定。

struct something { 
    something() 
     : p(new int(5)) 
    { } 

    std::unique_ptr<int> p; 
}; 

注意,天真代碼

struct something { 
    something() 
     : p(new int(5)) 
    { } 

    int* p; 
}; 

將是異常安全,也因爲只有一個分配,這可能會失敗。我認爲你說的是​​

struct something { 
    something() 
     : p(new int(5)), q(new int) 
    { } 

    int *p, *q; 
}; 

哪個不會。智能指針也適用於這種情況。

+0

唉,你是完全正確的;我更新了示例以實際觸發泄漏。 –

0

如果您只是處理異常情況,則根本不需要兩相結構。這是RIAA的方式。

struct something { 
    something() 
     : p1(NULL) 
     , p2(NULL) 
    { 
     p1 = new int(2); 
     try { 
      p2 = new int(5); // May throw if allocation fails! 
     } catch (std::bad_alloc&) { 
      delete p1; //cleanup 
      throw; //rethrow 
     } 
    } 

    ~something() { 
     delete p1; 
     delete p2; 
    } 

    int* p1; 
    int* p2; 
}; 
+0

如果我沒有完全弄錯,這是顯式**不是** RAII的方式,因爲你首先**用'NULL'初始化**'p1',然後**分配**一個'new int(2 )'。 – bitmask

+0

這使得'something'完成了RAII,儘管它的內部不符合RIAA。當你深入研究任何RIAA課程的內部時,最終你會選擇一個內部不符合RIAA的課程。我的觀點是安全的例外和缺乏兩階段的啓動。 'unique_ptr'也沒有RIAA成員。 –

+0

我明白了。我很抱歉。 :) – bitmask