私のC++プロジェクトには、Particle
とContact
の2つのクラスがあります。 Particle
クラスには、Particle
オブジェクトのすべての連絡先を含むメンバー変数_std::vector<Contact> contacts
_と、対応するメンバー関数getContacts()
およびaddContact(Contact cont)
。したがって、「Particle.h」には「Contact.h」を含めます。
Contact
クラスでは、Contact
のコンストラクターにコードを追加して、Particle::addContact(Contact cont)
を呼び出し、contacts
が両方のParticle
オブジェクトが追加されているContact
オブジェクト。したがって、「Contact.cpp」に「Particle.h」を含める必要があります。
私の質問は、これが許容できる/適切なコーディングプラクティスであるかどうかであり、そうでない場合は、達成しようとしていることを実装するためのより良い方法は何ですか(簡単に言えば、新しい連絡先のたびに特定の粒子の連絡先のリストを自動的に更新します)創造された)。
これらのクラスは、N個のパーティクル(_std::vector<Particle> particles
_)およびNcの接点(_std::vector<Contact> contacts
_)を持つNetwork
クラスによって結合されます。しかし、私はparticles[0].getContacts()
のような関数を使用できるようにしたかったのです。この場合、Particle
クラスにそのような関数を入れても大丈夫ですか、それともC++に「構造」というより良い関連付けがありますか。この目的(別のクラスで使用されている2つの関連クラスの)。
私がこれにどのように取り組んでいるのか、ここで視点のシフトが必要になるかもしれません。 2つのクラスはNetwork
クラスオブジェクトによって接続されているので、接続情報をNetwork
オブジェクトによって完全に制御することが典型的なコード/クラス編成です(パーティクルオブジェクトは、その連絡先、およびその結果、getContacts()
メンバー関数を含めることはできません)。次に、特定のパーティクルが何に接触しているかを知るために、Network
オブジェクトを通じて(たとえば、network.getContacts(Particle particle)
を使用して)その情報を取得する必要があります。
Particleオブジェクトがその知識を持つこと(つまり、NetworkオブジェクトまたはParticleオブジェクトのいずれかを介して、その情報にアクセスするための複数の方法を持つこと)は、あまり一般的ではない(おそらく推奨されない)C++クラスの設計でしょうか)?
質問には2つの部分があります。
最初の部分は、C++ヘッダーファイルとソースファイルの構成です。これはforward宣言を使用し、クラス宣言(ヘッダーファイルに置く)とメソッド本体(ソースファイルに置く)を分離することで解決されます。 )。さらに、場合によっては Pimplイディオム(「実装へのポインター」) を適用して、より困難なケースを解決できます。共有所有権ポインタ(shared_ptr
)、単一所有権ポインター(unique_ptr
)、およびベストプラクティスに基づく非所有ポインタ(生のポインタ、つまり「アスタリスク」)。
2番目の部分は、相互に関連するオブジェクトをgraphの形式でモデル化する方法です。 ではない一般的なグラフDAG(有向非循環グラフ)には、ツリーのような所有権を表現する自然な方法がありません。代わりに、ノードと接続はすべて、単一のグラフオブジェクトに属するメタデータです。この場合、ノード接続関係を集約としてモデル化することはできません。ノードは接続を「所有」しません。接続はノードを「所有」しません。代わりに、それらは関連付けであり、ノードと接続の両方がグラフによって「所有」されます。グラフは、ノードと接続を操作するクエリと操作のメソッドを提供します。
正解ですが、同じ連絡先オブジェクトは複数のパーティクルオブジェクトに属しています。これは、2つ以上のパーティクル間の何らかの物理的な連絡先を表しているからですよね。
だから私が最初に疑わしいと思うのは、Particle
にメンバー変数_std::vector<Contact>
_がある理由です。代わりに、_std::vector<Contact*>
_または_std::vector<std::shared_ptr<Contact> >
_にする必要があります。 addContact
は、代わりにaddContact(Contact *cont)
またはaddContact(std::shared_ptr<Contact> cont)
のような異なるシグネチャを持つ必要があります。
これにより、「Particle.h」に「Contact.h」を含める必要がなくなり、「Particle.h」に_class Contact
_の前方宣言が含まれ、「Particle.cpp」に「Contact.h」が含まれるようになります。足ります。
次に、コンストラクタについての質問。あなたは次のようなものが欲しい
_ Contact(Particle &p1, Particle &p2)
{
p1.addContact(this);
p2.addContact(this);
}
_
正しい?この設計は問題ありませんが、接触オブジェクトを作成する必要がある時点で、プログラムが常に関連する粒子を知っている限りです。
注:_std::vector<Contact*>
_ルートを使用する場合、Contact
オブジェクトの存続期間と所有権についていくつかの検討を行う必要があります。パーティクルはその連絡先を「所有」していないため、関連するParticle
オブジェクトが両方とも破棄された場合にのみ、連絡先を削除する必要があります。代わりに_std::shared_ptr<Contact>
_を使用すると、この問題が自動的に解決されます。または、「周囲のコンテキスト」オブジェクトにパーティクルと連絡先(@rwongによって提案されたような)の所有権を取得させ、その存続期間を管理させます。
考えられるもう1つのオプションは、パーティクル参照を受け入れるContactコンストラクターを作成することです。これにより、ContactはaddContact(Contact)
を実装するコンテナに自分自身を追加できます。
template<class Container>
Contact(/*parameters*/, Container& container)
{
container.addContact(*this);
}
はい、あなたが説明することは、すべてのContact
インスタンスがParticle
の連絡先のリストに含まれていることを確認する非常に許容できる方法です。
あなたがしたことは正しいです。
別の方法...すべてのContact
がリストに含まれるようにすることを目的とする場合は、次のことができます。
Contact
(プライベートコンストラクター)の作成をブロックします。Particle
クラスをフォワード宣言し、Particle
クラスをContact
の友達にするParticle
でContact
を作成するファクトリメソッドを作成します次に、contact
にparticle.h
を含める必要はありません。