2009-01-12 48 views
2

封裝對象並管理其生命週期的最佳方法是什麼?例如:我有一個類A,其中包含類型爲B的對象,並且是單獨負責的封裝對象的生命週期管理

解決方案1 ​​,克隆b對象,以確保只有一個能夠清理。

class A 
{ 
    B *b; 
public: 
    A(B &b) 
    { 
     this->b = b.clone(); 
    } 

    ~A() 
    { 
     delete b; // safe 
    } 
}; 

解決方案2,直接使用傳遞的對象,我們就可能一個潛在的雙重免費在這裏。

class A 
{ 
    B *b; 
public: 
    A(B *b) 
    { 
     this->b = b; 
    } 

    ~A() 
    { 
     delete b; // unsafe 
    } 
}; 

在我的實際案例中,解決方案#2最適合。但是,我不知道這是否被認爲是不好的代碼,因爲即使有記錄,有人可能不知道A的行爲。我能想到這些場景:

B *myB = new B(); 
A *myA = new A(myB); 
delete myB; // myA contains a wild pointer now 

或者,

B *myB = new B(); 
A *firstA = new A(myB); 
A *secondA = new A(myB); // bug! double assignment 
delete firstA; // deletes myB, secondA contains a wild pointer now 
delete secondA; // deletes myB again, double free 

可我只是忽視這些問題,如果我正確記錄A的行爲?宣佈責任並讓其他人閱讀文檔就足夠了嗎?這是如何在你的代碼庫中管理的?

回答

4

您應該定義您的對象,以便所有權語義儘可能地由接口定義。正如David Thornley指出的那樣,std :: auto_ptr是指示所有權轉移的智能指針。定義類,像這樣:

class A 
{  
    std::auto_ptr<B> b; 
public:  
    A(std::auto_ptr<B> b)  
    { 
     this->b = b; 
    } 
    // Don't need to define this for this scenario 
    //~A() 
    //{ 
    // delete b; // safe 
    //} 
}; 

由於性病:: auto_ptr的合同是所有權的分配對象=轉移,現在你的構造規定隱含指針的一個對象具有所有權到B它的通過。事實上,如果客戶端嘗試使用來執行std :: auto_ptr <B>,他們在構造之後用它來構造A,那麼操作將失敗,因爲它們所持有的指針將無效。

2

如果您正在編寫其他人將在以後使用的代碼,則必須解決這些問題。在這種情況下,我會去進行簡單的引用計數(也許用智能指針)。考慮下面的例子:

當一個封裝類的實例被分配了一個對象B時,它調用一個方法來增加對象的B引用計數器。當封裝類被銷燬時,它不會刪除B,而是調用一個方法來減少引用計數。當計數器達到零時,對象B被銷燬(或爲此而銷燬)。這樣封裝類的多個實例可以使用對象B的單個實例。

更多關於這個問題:Reference Counting

+0

所以,你會改變A的構造函數採取類似shared_ptr包含b? – driAn 2009-01-12 20:24:47

+0

不需要引用計數。如A所述,擁有所有權。 – 2009-01-12 20:37:39

1

如果您克隆了A,並且A1和A2都保留對B的引用,那麼B的生存期並不完全由A來控制。它在各個A之間共享。克隆B確保一對一關係As和Bs之間,這將很容易確保生命週期的一致性。

如果克隆B不是一個選項,那麼您需要放棄A負責B的生命週期的概念。另一個對象需要管理各種B,否則您需要實現一個像引用計數的方法。

作爲參考,當我想到術語'克隆'時,它意味着一個深層副本,它也會克隆B.我希望兩個As在克隆之後完全脫離彼此。

0

我不會不必要地克隆東西或「爲了安全起見」。

相反,我知道刪除某些東西的責任:通過文檔或智能指針......例如,如果我有一個create函數,它實例化某個東西並返回一個指向它的指針並且不刪除它,所以它不清楚在哪裏以及哪個東西被刪除了,然後代替create返回a裸指針我可能定義create的返回類型爲返回包含在某種智能指針內的指針。

2

如果您的對象完全負責傳遞的對象,那麼刪除它應該是安全的。如果這不是安全的,那麼你斷言你完全負責是錯誤的。那它是哪一個?如果您的界面記錄爲您將刪除入站對象,那麼確保您收到一個必須由您刪除的對象是主叫方的責任。

5

我從來沒有刪除任何東西,除非我真的必須。這導致錯誤。

智能指針是你的朋友。 std::auto_ptr<>是你的朋友,當一個對象擁有另一個對象並負責在超出範圍時刪除它。當有可能有多個對象附加到另一個對象時,並且希望在沒有更多對象引用它時刪除該對象,則可以使用boost::shared_ptr<>(或現在std::tr1::shared_ptr<>)。

因此,請使用您的解決方案1與auto_ptr,或者您的解決方案2與shared_ptr