2014-03-05 22 views
5

讓我們看一下表示子節點樹的數據結構(Node)的例子。每個對象的子節點集存儲在地圖中>shared_ptr垃圾桶的分配是否可以使用`this`指針

class Node; 
typedef std::shared_ptr<Node> NodePtr; 

class Node 
{ 
    std::map<const std::string, NodePtr> _childNodes; 
    void SomeOtherMethod(); 

public: 
    bool GetChildByKeyName(/*In*/ const std::string& key, /*Out*/ NodePtr& spChild) 
    { 
     bool result = false; 
     auto itor = _childNodes.find(key); 
     if (itor != _childNodes.end()) 
     { 
      spChild = itor->second; 
      result = true; 
      SomeOtherMethod(); 
     } 
     return result; 
    } 
}; 

並且以下用戶代碼示例通常會調用它。

NodePtr spNode, spChildNode; 
bool result; 
... 
result = spNode->GetChildByKeyName(strChildKeyName, spChildNode); 

到目前爲止好。

它發生,我認爲呼叫者可能的方式來遍歷樹,而不必應付額外的變量對每個深入到樹

NodePtr spNode; 
bool result; 

result = spNode->GetChildItem(strChildKeyName, spNode); 
if (result) 
    spNode->GetChildItem(strSubKeyName, spNode); 

在上述情況下,如果spNode是最終剩餘參考對象的話,我很擔心在GetChildItem方法的代碼塊:

  spChild = itor->second; 
      result = true; 
      SomeOtherMethod(); 

是否spChild的分配(這是真的來電者的spNode實例)無意中破壞「這個」點頭e自上次參考文獻剛剛離開? (因此在spChild賦值之後調用其他方法是危險的)。我在這裏有潛在的錯誤嗎?

我認爲解決方法是簡單地在方法調用的頂部添加這一行:

NodePtr spChildRef = spChild; // maintain a reference to the caller's original node during the scope of the method 

的思考?

+0

如果它確實是垃圾,那麼它會是最奇怪的,也許最難檢測到'刪除這個;'我會遇到。 –

+0

@Mat - spNode將是這種特殊情況下的根節點,其refcounf爲1. – selbie

回答

6

如果第二個示例中最外面的spNode指針是對根項目的唯一引用,則GetChildByKeyName將替換導致該對象被破壞的引用(實質上是「刪除此」),這是正確的。

我意識到這可能不是完整的代碼,可能有這樣的原因,你爲什麼設計它,但我個人建議改變接口返回找到的孩子,而不是使用out參數。 (你可以成功之間通過測試空發現孩子還是區分&失敗。)

不僅成爲實際查找代碼更簡單:

NodePtr GetChildByKeyName(/*In*/ const std::string& key) 
{ 
    auto itor = _childNodes.find(key); 
    if (itor != _childNodes.end()) 
    { 
     SomeOtherMethod(); 
     return itor->second; 
    } 
    return nullptr; 
} 

然後,您可以還重用指針你的心臟的內容:

NodePtr spNode; 
.... 

spNode = spNode->GetChildItem(strChildKeyName); 
if (spNode) 
    spNode = spNode->GetChildItem(strSubKeyName); 
+0

謝謝!無論出於何種原因,這個簡單的想法從來沒有出現過我在C++方面有很長的歷史,但在職業生涯開始時,老派的C++大師教會我永遠不要通過堆棧返回對象,以避免複製構造函數的開銷,堅持使用指針作爲參數,等等。我目前的產品組更具有進步性。所以這個想法效果更好。 – selbie

3

+1至@Dentoid's answer。我不打算在這裏複製他的答案。我只會顯示你現有的代碼是否有問題。


能一個shared_ptr垃圾的分配新建分配FY的this指針?

是的,它的確如此。

我做了一個測試,以確定它:http://coliru.stacked-crooked.com/a/ef0d4f92902b4dee

它的輸出(格式爲清楚起見):

spNode before: 0x15a1028 
    this: 0x15a1028 <---------------------------------------------------------| 
    spChild.get() == this              | 
                      | 
    spChild before: 0x15a1028 <-------- Notice: They're the same -------------| 
     Node 0x15a1028 destroyed.            | 
      ^---------------------------------------------------------------| 
    spChild after: 0x15a1078             | 
                      | 
    SomeOtherMethod() @0x15a1028; size: 1 |         | 
spNode after: 0x15a1078  ^------------------------------------------------| 

Node 0x15a1078 destroyed. 

所以SomeOtherMethod()被稱爲已經銷燬的對象上,雖然運行時間一直沒有能夠檢測到這一點。

應用你的工作,各地,http://coliru.stacked-crooked.com/a/f0042d4b46fed340

spNode before: 0x19a7028 
    this: 0x19a7028 
    spChild.get() == this 

    spChild before: 0x19a7028 
    spChild after: 0x19a7078 

    SomeOtherMethod() @0x19a7028; size: 1 

    Node 0x19a7028 destroyed. 
spNode after: 0x19a7078 

Node 0x19a7078 destroyed. 

它不會有問題了。

+0

謝謝馬克。我給了你一個驗證這個的upvote。你證實了我的懷疑。 – selbie

+2

只是想指出,「雖然運行時尚未能檢測到」這是有點誤導。它通常不是真的關於任何運行時檢測任何東西,而是關於「this」指向的內存被釋放以被其他事物重用。它可能會工作(因爲在這種情況下它可能不會被覆蓋),但它可能不會,但是使用內存在此時未定義並且需要避免。 – Dentoid