2016-05-31 9 views
5

私はGotWのような主要なC++フォーラム/ブログで説明されている循環参照を壊す次の方法をまだ見つけられていないので、その技術が知られているかどうか、その賛否両論は何か?外部から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を生きたままにします。

私はここで何かを見落としているのですか?これは既に文書化されている循環参照を壊す明白な方法ですか?あなたのgetParentによって返さ

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_B 'を作成し、' left_A-> shared_from_this'を使用すると、間接参照がツリーの上に続き、参照カウントがまだ 'parent'ですか?その場合、すべてのノードが同じ参照カウントを共有するため、ツリー全体が削除されるまで、ノードを削除してリソースをリサイクルすることはできません。 –

+0

@SteveLorimer彼らは、ノードの 'get'関数の1つによって外界に与えられていない限り、ルートの参照カウントを共有しません(別のノードによって与えられたものです。その根)。誰かが子ノードへの参照を持っていない限り(おそらく、非同期操作中にツリーを歩いているときに一時的にしか起こらない)、参照カウントは共有されず、リソースを解放することができます。 –

答えて

3

shared_ptr<Node>は親ではなく、親の親を所有しています。

したがって、shared_ptrにもう一度getParentを呼び出すと、空(null)の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

子ノードのクリアを再度サポートしたくない限り、それは砂漠化をもたらすだろう –

関連する問題