2016-05-31 22 views
5

我還沒有找到下面的方式來打破任何主要的C++論壇/博客上解釋的循環引用,就像在GotW上一樣,所以我想問一下這個技術是否已知,它的優缺點是什麼?用std :: weak_ptr和別名構造函數打破循環引用:聲音還是有問題?

class Node : public std::enable_shared_from_this<Node> { 
public: 
    std::shared_ptr<Node> getParent() { 
     return parent.lock();  
    } 

    // the getter functions ensure that "parent" always stays alive! 
    std::shared_ptr<Node> getLeft() { 
     return std::shared_ptr<Node>(shared_from_this(), left.get()); 
    } 

    std::shared_ptr<Node> getRight() { 
     return std::shared_ptr<Node>(shared_from_this(), right.get()); 
    } 

    // add children.. never let them out except by the getter functions! 
public: 
    std::shared_ptr<Node> getOrCreateLeft() { 
     if(auto p = getLeft()) 
      return p; 
     left = std::make_shared<Node>(); 
     left->parent = shared_from_this(); 
     return getLeft(); 
    } 

    std::shared_ptr<Node> getOrCreateRight() { 
     if(auto p = getRight()) 
      return p; 
     right = std::make_shared<Node>(); 
     right->parent = shared_from_this(); 
     return getRight(); 
    } 

private: 
    std::weak_ptr<Node> parent; 
    std::shared_ptr<Node> left; 
    std::shared_ptr<Node> right; 
}; 

從外面看,的Node用戶不會在getLeftgetRight使用別名構造注意到的伎倆,但仍是用戶可以肯定的是getParent總是返回一個非空的共享指針,因爲所有由p->get{Left,Right}返回的指針使對象*p在返回的子指針的生命週期中保持活動狀態。

我在這裏忽略了什麼,或者這是一個明顯的方式來打破已被記錄的循環引用?

int main() { 
    auto n = std::make_shared<Node>(); 
    auto c = n->getOrCreateLeft(); 
    // c->getParent will always return non-null even if n is reset()! 
} 
+0

這是否意味着最終*所有孩子*都會與根共享相同的引用計數?也就是說,如果我使用別名構造函數創建'left_A',那麼它的引用計數與'parent'相同。那麼,如果我從'left_A'開始創建一個新的'left_B',並且使用'left_A-> shared_from_this' - 引用計數仍然是'parent',因爲在間接繼續樹?在這種情況下,所有節點共享相同的引用計數,並且在刪除整個樹之前,您永遠不能刪除節點並回收其資源? –

+0

@SteveLorimer他們不會共享根的引用計數,除非它們已經被一個節點的get函數賦予了外部世界(這又是另一個節點賦值的,等等,從根)。因此,除非有人引用了一個子節點(這可能只是在異步操作期間走樹時的情況),引用計數不會共享,並且可以釋放資源。 –

回答

3

getParent返回的shared_ptr<Node>擁有父,沒有父親的父親。

因此,再次呼籲getParentshared_ptr可以返回一個空(和空)shared_ptr。例如:

int main() { 
    auto gp = std::make_shared<Node>(); 
    auto p = gp->getOrCreateLeft(); 
    auto c = p->getOrCreateLeft(); 
    gp.reset(); 
    p.reset(); // grandparent is dead at this point 
    assert(c->getParent()); 
    assert(!c->getParent()->getParent()); 
} 

(繼承shared_from_this也通過了shared_ptr s表示擁有該節點而不是其父母,但我想你可以通過私人使用聲明使其難以弄亂並在簽訂合同禁止它。 )

+0

啊,這很糟糕,好點! –

+0

我認爲它工作,如果你存儲一個弱指針,既父和根,並在get函數傳遞根指針到別名構造函數,對不對? –

+0

只要你不想再支持清除子節點。那會導致災難 –