2011-11-15 4 views
9

私はDAGの一部となるクラスを作成しています。コンストラクタは他のインスタンスへのポインタをとり、それらを使用して依存関係リストを初期化します。
依存関係リストが初期化された後は、これを短くすることができます。インスタンスは、それ自体またはその子の依存関係として追加することはできません。私のコンストラクタでこれにweak_ptrsを渡すにはどうしたらいいですか?

::std::shared_ptrはこれを処理するための自然です。 DAGを処理するための参照カウントが作成されました。

残念ながら、依存関係は依存関係を知る必要があります。依存関係が更新されると、その依存関係のすべてを伝える必要があります。
これは、::std::weak_ptrで壊れる可能性のある簡単なサイクルを作成します。依存関係は、去っていく扶養家族についてただ忘れることができます。

しかし、それが構築されている間、従属者が自分自身に::std::weak_ptrを作成する方法を見つけることができません。

これは動作しません:デストラクタで

object::object(shared_ptr<object> dependency) 
{ 
    weak_ptr<object> me = shared_from_this(); 
    dependency->add_dependent(me); 
    dependencies_.push_back(dependency); 
} 

このコードの結果はコンストラクタが終了する前に呼び出されます。

この問題を処理する良い方法はありますか?私はC++ 11のみのソリューションに満足しています。

答えて

9

コンストラクタの代わりに、関数を使用してグラフのノードを構築します。

std::shared_ptr<Node> mk_node(std::vector<std::shared_ptr<Node>> const &dependencies) 
{ 
    std::shared_ptr<Node> np(new Node(dependencies)); 
    for (size_t i=0; i<dependencies.size(); i++) 
     dependencies[i].add_dependent(np);  // makes a weak_ptr copy of np 
    return np; 
} 

あなたはこのstaticメンバ関数や、あなたのNodeクラスのfriend作る場合、あなたは実際のコンストラクタprivateを作ることができます。

+2

これは、 'enable_shared_from_this'がすでに行っていることです。あなたは宣言に迷子*があるので、shared_ptrへのポインタではなく、shared_ptrが必要です。 :) –

+0

これは良い答えですが、私の状況では私は「扶養者」の議論を持っていません。この関数は、新たに作成されたノード(すなわち、 'np')を各依存関係の従属リストに追加するすべての依存関係をループします。 – Omnifarious

+0

@Omnifarious:例を更新しました。 –

5

基本的にはできません。 weak_ptrを作成するには、shared_ptrまたはweak_ptrが必要です。また、自分自身はのみをweak_ptrの形式でしか認識できないことは明らかです(そうでなければ、数え方の参照点がありません)。そして、もちろん、オブジェクトがまだ構築されていないときに自己shared_ptrが存在しない可能性があります。

1

私はあなたの質問が概念的にはgarbage collectionの問題に関連していると考えています。

GCは非モジュール化された機能です:プログラムのいくつかのグローバルプロパティを扱います(より正確には、ライブデータはプログラム内のグローバルでモジュール化されていないプロパティです。それはモジュラーで&構成方法です。 AFAIK、STLまたはC++の標準ライブラリは、グローバルプログラムの機能についてはあまり役に立ちません。

ガベージコレクタを使用する(または自分で実装する)可能性があります。 Boehm's GCは、あなたがプログラム全体でそれを使用できるのであれば、あなたにとって有用かもしれません。

また、問題に対処するためにGCアルゴリズムを使用することもできます(世代のものをコピーすることもできます)。

+0

これがどのように質問に答えるかは本当に分かりません。 –

+0

あなたのものは、最も使い慣れた答えです。興味深いことに、私はすでにそれを考え、そのアイデアを捨てました。しかし、ええ...あなたは正しい。残念ながら、これは他の人にとって再利用可能なライブラリの一部として意図されており、GCを必要とするのは多くのC++の人たち(しばしば私を含む)の初心者ではありません。 – Omnifarious

+0

@BillyONeal:Basileは 'shared_ptr'参照カウントを落とし、代わりに本格的なガベージコレクタを使うことを提案しています。 – Omnifarious

0

多分これは役立ちます:

は基本的にweak_ptrを保持しているenable_shared_from_thisから継承します。それについて オブジェクトを指しているとき、これはあなたがthis->shared_from_this();

のshared_ptrのクラスは、クラスから継承場合見て、クラスを使用するために知って使用できるようになりますweak_ptrを(別々に参照を数えてから2つの共有ポインタを防止する)

詳細:cppreference

+0

すでに 'shared_ptr'を所有していない限り、shared_from_thisを呼び出すことはできません。コンストラクタ内にはありません。(20.7.2.4/7) –

+1

+1はenable_shared_from_thisです。基本的に、OPがとにかく望んでいるように、weak_ptrメンバとして実装されています。 –

+0

@AlanStokes:できません。しかし、OPのケースを見ると、オブジェクト自体に格納されているオブジェクトへの弱いポインタを必要としているように思えます。これはまさにenable_shared_from_thisが行うものです。 –

1

できません。

ベストは、コンストラクタをプライベートにして、shared_ptrを新しいオブジェクトに返すパブリックファクトリ関数を持つことです。ファクトリ関数は、weak_ptrを初期化するポストコンストラクションのプライベートメソッドを呼び出すことができます。

+0

クラスを派生させる場合、コンストラクタはプライベートではなく保護する必要があります。 – Omnifarious

+0

@Omnifariousはい、もちろんです。 –

1

残念ながら、依存関係はその扶養者を知る必要があります。これは、依存関係が更新されると、その依存関係のすべてを伝える必要があるためです。そして、些細なサイクルがあります。幸いなことに、このサイクルは:: std :: weak_ptrで壊れる可能性があります。依存関係は、去っていく扶養家族についてただ忘れることができます。

依存関係が存在しない限り、依存関係は依存関係を参照できません。たとえば、依存関係が破棄されても依存関係は破棄されます(つまり、DAGと同様)

その場合、単純なポインタ(この場合はthis)を渡すことができます。依存が死んでいなければ依存が死んだはずだから、依存が生きていれば依存関係をチェックする必要はありません。

オブジェクトがshared_ptrによって所有されているからといって、それ自体のすべてのポインタ自体がshared_ptrsまたはweak_ptrsでなければならないわけではありません。ポインタが無効になったときに明確なセマンティクスを定義する必要があります。

+0

オブジェクトが複数の扶養家族を持つことができない場合、これは問題ありません。これはツリーではなくDAGです。ブランチは一緒に戻って参加することができます、ちょうどサイクルができません。 – Omnifarious

+0

@Omnifarious:Ok。 Factory Methodはあなたの唯一のオプションです。これは、静的または自動ストレージではなく 'shared_ptr'を使用してオブジェクトを構築することを強制する唯一の方法です。 (プラス側では、工場でのmake_sharedによる最適化の種類を利用することができます)。 –

1

単一のオブジェクト(DAG内のノード)とそれらのオブジェクトのコレクションを管理するなど、多少異なるアイテムにコンバートしようとしているように聞こえます。

class DAG { 

    class node { 
     std::vector<std::weak_ptr<node> > dependents; 
    public: 
     node(std::vector<weak_ptr<node> > d) : dependents(d) {} 
    }; 

    weak_ptr<node> root; 
}; 

今は、DAGがしかむしろ直接nodeのインスタンスを扱うよりも、weak_ptr<node>を保持することは事実かもしれません。しかし、ノード自体には、これは多かれ少なかれ無関係です。それは、それ自身の扶養家族のリストと一緒に含まれるどんなキー/データ/ etcでも維持する必要があります。

同時に、DAG内にネストすることで(特に、nodeのクラス定義をDAGに限定した場合)、nodeへのアクセスを最小限に抑えることができるため、他のコードではnode。状況によっては、コンパイラがデフォルトで生成するノードの関数(ほとんどの?)を削除すること(たとえば、デフォルトのctor、copy ctor、代入演算子)を行うこともできます。

+0

これは興味深い答えです。私はこのように私のデザインを変えることができるかどうか分からない。私は、マルチスレッドの将来/約束のような配置を扱うための依存関係に基づくシステムを構築しようとしています。ノードはかなり重要な存在です。 OTOHには、特定のスレッドの一部であるすべてのノードのマネージャが存在します。うーん... – Omnifarious

0

これも私をナットにしています。

私は、サイクルを中断するためのポインタを使用する方針を採用することを検討しました...しかし、私は本当にweak_ptrの意図があなたのコードでそれを見るときにどれほど明確であるかが好きなので、そこにサイクルを壊す)。

今私は自分自身のweak_ptrクラスの作成に傾いています。

+0

_chuckle_私はコンストラクタ関数の作成を終了しました。しかし、それは別の興味深い問題があります:http://stackoverflow.com/questions/8147027/how-do-i-call-stdmake-shared-on-a-class-with-only-protected-or-private-constこれは、スタックや埋め込みオブジェクトとしてこれらのものを作成し、それらに 'shared_ptr'を作成することを防ぐという利点があります。もし人々がそれをしたら、それは大量のものを壊すだろう。私はそれもあなたの場合かもしれないと思う。 – Omnifarious

関連する問題