3

メッシュに対していくつかの幾何学的操作を行う読み取りコードが与えられました。

定義上、メッシュ データ構造には、少なくともポイントの座標、エッジの接続性、面の情報に関する情報が含まれている必要があります。

したがって、私に与えられたコードには、頂点、エッジ、面のデータ構造を定義するクラスがあり、それぞれ頂点、エッジ、面と名付けられています。

ただし、メッシュ クラスは次のようになります。

class basemesh
{
public:
  /* Methods to operate on the protected data below.*/

protected:
   /*! list of edges */
  std::list<Edge*>           m_edges;

  /*! list of vertices */
  std::list<Vertex*>         m_verts;

  /*! list of faces */
  std::list<Face*>       m_faces;

}

私の質問: メッシュ データ構造は、対応するオブジェクト自体のリストではなく、ポインターのリストを格納するのはなぜですか。

例:直接言わない理由 std::list<Vertex>

この構造が他のいくつかの C++ コードで使用されているのを見てきました。

これはクラスの継承と関係がありますか? それとも、リストの反復に関するパフォーマンスと関係がありますか?

この basemesh クラスは、名前が示すように、他の特殊なメッシュが派生する基本クラスです。

4

7 に答える 7

2

ここにはパフォーマンス上の理由はありません。所有権の共有の単純なケースです。経験則としてこれを覚えておいてください: C++ のポインターは、リソースの所有権を共有/渡すために、または動的バインディングを通じてポリモーフィックな動作を提供するために使用されます。

人々がパフォーマンスについて話しているのは、あなたが物事を真似することを避けているからです。何とか何とか何とか。 コピーする必要がある場合は、コピーする必要があります。ポインターを使用する唯一の理由は、作者が物のリストをコピーするときに物をコピーしたくなかったためです。つまり、同じものを 2 つの場所 (リスト) で維持したいからです。前に言ったように、共有します。

一方、クラスは と呼ばれることに注意してくださいbasemesh。したがって、ここでのポインタの本当のポイントは、ポリモーフィックな頂点、エッジなどを操作することです (動的バインディング)。

注:std::vectorここでパフォーマンスがポイントである場合、作成者はの代わりにコンパクトでアラインされた非キャッシュ ミス プローンを使用していると確信していstd::listます。この場合、ポインターの使用に関する最も推定される理由は、パフォーマンスではなくポリモーフィズムです。ポインター、逆参照、リンクされたリストの横断に関連するものはすべて、コンパクト データよりも常にパフォーマンスが低くなりますstd::vector<Vertex>繰り返しますが、ポインターの使用がポリモーフィズムのためではなく、所有権に関連するものであり、パフォーマンスではありません。

その他の注記: コピーはい、コピーしています。ただし、何をどのようにコピーしているかに注意してください。頂点は、非常にまれな実装を除いて、float/int のペアです。float の 64 ビットと 32/64 ビットのポインターをコピーしても、まったくメリットはありませんまた、運が悪ければ、同じキャッシュ ラインまたはほぼキャッシュに保存されているものをコピーしていること
にも注意してください。

現在の最適化に関する適切なルールは、次のとおりです。CPU サイクルではなく、メモリ アクセスを最適化してください。このスレッドをお勧めします: 「キャッシュに適した」コードとは? 、そしてこれは実用的なケースです:要素ごとの追加は、結合されたループよりも個別のループの方がはるかに高速なのはなぜですか? . 最後に、このスレッドには、最新のコンパイラを使用した最適化に関する優れたメモが含まれています。

于 2013-11-05T21:17:10.500 に答える
1

私の推測では、非常に珍しい特定のケースのために作成されたものですが、ヒープ割り当ての方法やstd::list実際に機能する方法を知らず、やみくもにポインターを使用するプログラマーによって作成された可能性が高いです。

単一の頂点へのポインターがパフォーマンス上または設計上の最良のオプションであった可能性は非常に低いようです。std::list

于 2013-11-05T21:55:25.723 に答える
0

実際的なレベルでは、メソッドがポイントを変更した場合、他のデータ構造で変更を再現する必要はありません。それらはすべて同じことを指します。

しかし、メモリ管理に関しては、スマート ポインターを使用するのが賢明でしょう。

于 2013-11-05T20:38:43.123 に答える
0

推測では、これらのオブジェクトが相互にポインターを持つことができるようにするためだと思います (たとえば、Edge は 2 つの Vertices へのポインターを持つことができ、それぞれが Edge に戻るポインターを持つことができます)。

すべての頂点が basemesh の std::list に存在する場合、それらへのポインターは信頼できませんが、list::iterator は十分に機能する可能性があります。

于 2013-11-05T20:39:04.937 に答える
0

アクセスするたびに値を逆参照する必要があるため、一般に内部データを取得する場合、ポインターを使用すると効率が低下します。

しかし同時に、ポインタを渡すだけなので、データを渡すときはより効率的になります。選択されたソリューションは、構成によって複数のオブジェクト間でデータが共有されるという事実に関連していると思います。例: 複数のEdgeインスタンスが同じ を参照できますVertex

std::list要素自体が削除されるまで、含まれる値へのアドレスが一貫していることを保証するようになったため、実際には次のようなことを行います

Edge(const Vertex *v1, const Vertex *v2) { .. }

std::list<Vertex>::iterator it = std::advance(vertices.begin(), 3);
std::list<Vertex>::iterator it2 = std::advance(vertices.begin(), 5);
new Edge(&(*it), &(*it2));

アドレスは無効にされないため、オブジェクトを格納するためにポインターを使用する必要はありません。実際には、このソリューションを使用することで、単一オブジェクトのメモリ管理を気にする必要がなくなりdeleteます。それらをスマート ポインターにラップしたりラップしたりする必要がないからです。

于 2013-11-05T20:40:40.023 に答える
-1

他の多くの人が言ったように、速度が最も明白な理由です。もう 1 つの理由は、基本クラスへのポインターを介してポリモーフィックな動作を取得することです。

于 2013-11-05T20:59:46.073 に答える
-1

パフォーマンス上の理由とエラーの可能性を減らすためにポインターを使用しています。

ポインターを使用しないという代替案を想像してください。クラス basemesh に挿入するたびに、オブジェクトのコピーが作成され、オブジェクトにアクセスするたびに、注意しないとコピーも取得されます。

たとえば、次のステートメントを想像してください。

Edge e = m_edges[0];
e.doSomethingThatModifiesState();

この例では、ポインターがないため、オブジェクトのコピーが作成され、それに対して実行する操作は、m_edges に格納されている実際のエッジ オブジェクトには影響しません。

ポインターを使用すると、この問題は発生しません。

Edge* e = m_edges[0];
e->doSomethingThatModifiesState();

この例では、オブジェクトのコピーは作成されず、何かを実行すると意図した動作が得られます。

于 2013-11-05T20:40:02.533 に答える