23

私はC++を初めて使用します。MichaelDawsonによる「BeginningC++ThroughGameProgramming」を読んでいます。しかし、私は一般的にプログラミングに不慣れではありません。ベクトルを扱った章を終えたばかりなので、実世界でのベクトルの使用について質問があります(私はコンピューターサイエンスの学生なので、実世界での経験はまだあまりありません)。

著者は各章の終わりにQ/Aを持っており、そのうちの1つは次のとおりです。

Q:配列の代わりにベクトルを使用する必要があるのはいつですか?

A:ほとんどの場合。ベクトルは効率的で柔軟性があります。アレイよりも少し多くのメモリが必要ですが、このトレードオフはほとんどの場合、メリットに見合う価値があります。

皆さんはどう思いますか?Javaの本でベクトルについて学んだことを覚えていますが、Compの紹介ではベクトルについてはまったく取り上げませんでした。科学 クラスでも、大学のデータ構造クラスでもありません。また、プログラミングの割り当て(JavaおよびC)でそれらが使用されるのを見たことがありません。学校のコードと実際のコードは非常に異なる可能性があることは知っていますが、これはあまり使用されていないように感じます。

2つのデータ構造の違いについて説明する必要はありません。私はそれらをよく知っています。私が知りたいのは、作者がQ / Aで良いアドバイスをしているのか、それとも、固定サイズのデータ​​構造を管理する複雑さで初心者プログラマーが自分自身を破壊するのを防ごうとしているのかということだけです。また、著者のアドバイスについてどう思うかに関わらず、現実の世界でより頻繁に見られるものは何ですか?

4

7 に答える 7

26

A: ほとんどの場合 [配列の代わりにベクトルを使用]。ベクトルは効率的で柔軟です。それらは配列よりも少し多くのメモリを必要としますが、このトレードオフはほとんどの場合、メリットに値します。

それは過度の単純化です。配列を使用することはかなり一般的であり、次の場合に魅力的です。

  • 要素はコンパイル時に指定されconst char project[] = "Super Server";ますconst Colours colours[] = { Green, Yellow }

    • std::vectorC++11 では、 s を値で初期化するのも同様に簡潔になります。

  • 要素の数は本質的に固定されています。たとえばconst char* const bool_to_str[] = { "false", "true" };Piece chess_board[8][8];

  • 初回使用時のパフォーマンスは重要です。定数の配列を使用すると、コンパイラは多くの場合、完全に事前に初期化されたオブジェクトのメモリ スナップショットを実行可能イメージに書き込むことができます。実行可能イメージは、すぐに使用できる場所に直接ページ フォールトされます。実行時のヒープ割り当て ( new[]) に続いて、オブジェクトのシリアライズされた構築

    • コンパイラで生成されたconstデータのテーブルは、常に複数のスレッドで安全に読み取ることができますが、実行時に構築されたデータは、関数ローカルstatic変数以外のコンストラクターによってトリガーされた他のコードがそのデータを使用しようとする前に構築を完了する必要があります。何らかの方法が必要になります。シングルトンの(おそらくさらに遅くなるスレッドセーフ)

    • C++03 ではvector、初期サイズで作成された s は、1 つのプロトタイプ要素オブジェクトを構築してから、各データ メンバーをコピー構築します。これは、構築が故意に操作なしのままにされた型であっても、データ要素をコピーするコストが依然として存在することを意味していました。明らかに、初期化されていない要素の配列の方が高速です。

  • C++ の強力な機能の 1 つは、多くの場合、特定のプロトコルに必要なメモリ レイアウトを正確にモデル化するclass(またはstruct) を記述し、値を便利に解釈または割り当てるために必要なメモリにクラス ポインタを向けることができることです。良くも悪くも、そのようなプロトコルの多くは、小さな固定サイズの配列を埋め込むことがよくあります。

  • 構造体/クラスの末尾に 1 要素 (またはコンパイラが拡張として許可する場合は 0) の配列を配置し、より大きなデータ領域で構造体型へのポインターを狙い、アクセスするための数十年前のハックがあります。メモリの可用性とコンテンツの事前知識に基づいて、構造体の末尾から配列要素 (書き込み前に読み取る場合) -要素がゼロの配列の必要性は何ですか? を参照してください。

  • 配列を含むクラス/構造体は引き続き POD 型にすることができます

  • 配列は、複数のプロセスからの共有メモリへのアクセスを容易にします (デフォルトでは、動的に割り当てられた実際のデータへの の内部ポインタは、共有メモリ内に存在せず、プロセス間で意味のあるものにもなりません。また、C++03に共有メモリvectorを強制的に使用させることは非常に困難でした)vectorカスタム アロケーター テンプレート パラメーターを指定する場合も同様です)。

  • 配列を埋め込むと、メモリ アクセス要件をローカライズできるため、キャッシュ ヒットが改善され、パフォーマンスが向上します。

vectorとはいえ、 (コードの簡潔さ、読みやすさ、またはパフォーマンスの点で)を使用するのが積極的な苦痛でない場合は、そうすることsize()をお勧めしat()ます。またvector、必要に応じて他の標準コンテナに変更する方が簡単であることが多く、標準アルゴリズムを適用する方が安全/簡単です (どの日x.end()よりも優れてx + sizeof x / sizeof x[0]います)。

更新: C++11 では、いくつかの利点と API 機能を提供しながら、sstd::array<>のコストの一部を回避する が導入されました。 cppreference.com/w/cpp/container/array .vector

于 2011-06-24T07:21:17.220 に答える
20

vector配列ではなくa を使用する最も良い理由の 1 つは、 RAIIのイディオムです。基本的に、C++ コードを例外セーフにするには、動的に割り当てられたメモリやその他のリソースをオブジェクト内にカプセル化する必要があります。これらのオブジェクトには、これらのリソースを解放するデストラクタが必要です。

例外が処理されない場合、呼び出されることが保証されているのは、スタック上のオブジェクトのデストラクタだけです。オブジェクトの外部にメモリを動的に割り当て、キャッチされない例外が削除される前にどこかでスローされると、メモリ リークが発生します。

の使用を覚える必要がなくなる良い方法でもありますdelete

およびその他の STL コンテナーstd::algorithmに多くの一般的なアルゴリズムを提供するもチェックしてください。vector

vector振り返ってみると、おそらくネイティブ配列の方が優れていたでしょう。しかし、これらすべての場合において、aBoost::multi_arrayまたは a のどちらBlitz::Arrayかがどちらよりも優れていたでしょう。

于 2011-06-24T03:37:30.407 に答える
6

std::vector はサイズ変更可能な単なる配列です。それ以上のことはありません。これは、インテリジェントなデータ構造ではないため、データ構造クラスで学習するものではありません。

現実の世界では、多くの配列を目にします。しかし、「C with Classes」スタイルの C++ プログラミングを使用するレガシー コードベースも多数見られます。それは、そのようにプログラムする必要があるという意味ではありません。

于 2011-06-24T03:34:46.133 に答える
5

科学と工学で使用される大きなサイズの配列/ベクトルのコーディングについて、ここで私の意見を述べます。

この場合のポインターベースの配列は、特に標準型の場合、かなり高速になる可能性があります。しかし、ポインタはメモリ リークの可能性を高めます。これらのメモリ リークにより、デバッグ サイクルが長くなる可能性があります。さらに、ポインターベースの配列を動的にしたい場合は、これを手動でコーディングする必要があります。

一方、ベクトルは標準型では遅くなります。また、動的に割り当てられたポインターを stl ベクトルに格納しない限り、それらは動的でメモリセーフでもあります。

科学と工学では、選択はプロジェクトによって異なります。速度とデバッグ時間はどれくらい重要ですか? たとえば、シミュレーション ソフトウェアである LAAMPS は、メモリ管理クラスを通じて処理される生のポインターを使用します。このソフトウェアは速度が優先されます。私が構築しているソフトウェアは、メモリフットプリントとデバッグ時間で速度のバランスを取る必要があります。デバッグに多くの時間を費やしたくないので、STL ベクトルを使用しています。

この回答に、大規模な配列の広範なテストと Web の多くの読み取りから発見した情報を追加したいと思いました。そのため、stl ベクトルと大きなサイズの配列 (100 万以上) に関する別の問題は、これらの配列にメモリが割り当てられる方法で発生します。Stl ベクトルは、メモリの処理に std::allocator クラスを使用します。このクラスは、プール ベースのメモリ アロケータです。小規模なロードでは、プール ベースの割り当ては、速度とメモリ使用の点で非常に効率的です。ベクトルのサイズが数百万に達すると、プール ベースの戦略はメモリを大量に消費します。これは、プールが stl ベクトルによって現在使用されているよりも多くのスペースを常に保持する傾向があるために発生します。

大規模なベクトルの場合は、独自のベクトル クラスを作成するか、ポインター (生の、またはブーストまたは c++ ライブラリのメモリ管理システムのようなもの) を使用することをお勧めします。どちらのアプローチにも長所と短所があります。選択は、実際に取り組んでいる正確な問題に依存します (変数が多すぎてここに追加できません)。独自のベクター クラスを作成する場合は、ベクターのメモリを簡単にクリアできるようにしてください。現在、Stl ベクトルの場合、最初にクラスに組み込む必要があることを行うには、スワップ操作を使用する必要があります。

于 2014-03-09T22:36:13.293 に答える
4

経験則: 要素の数が事前にわからない場合、または要素の数が多い (たとえば 10 を超える) ことが予想される場合は、vector を使用します。それ以外の場合は、配列を使用することもできます。たとえば、私は多くのジオメトリ処理コードを書き、ラインを 2 つの座標の ARRAY として定義しています。線は 2 点で定義され、常に正確に 2 点で定義されます。配列の代わりにベクトルを使用すると、多くの点でやり過ぎになり、パフォーマンス面でも問題が発生します。

別のこと: 「配列」と言うとき、私は実際には配列を意味します: のような配列構文を使用して宣言された変数int evenOddCount[2];ベクトルと動的に割り当てられたメモリ ブロック ( など) のどちらを選択するかを検討する場合int *evenOddCount = new int[2];、答えは明らかです: USEベクター!

于 2011-06-24T07:28:33.467 に答える
3

既知のサイズの固定コレクションを扱うのは、現実の世界ではまれなケースです。ほとんどの場合、プログラムで扱うデータセットの正確なサイズはある程度不明です。実際、考えられる幅広いシナリオに対応できることは、優れたプログラムの特徴です。

例として、次の (些細な) シナリオを取り上げます。

  • FPS で AI 戦闘員を追跡するビュー コントローラーを実装しました。ゲーム ロジックは、数秒ごとにさまざまなゾーンにランダムな数の戦闘員を生成します。プレイヤーは、実行時にのみわかるレートで AI 戦闘員をダウンさせています。
  • ある弁護士が彼の州の地方裁判所のウェブサイトにアクセスし、一晩で入ってきた新しい DUI 事件の数を調べています。彼は、事故が発生した時間、郵便番号、逮捕した警官など、一連の変数でリストをフィルタリングすることを選択しました。
  • オペレーティング システムは、実行中のさまざまなプログラムが使用するメモリ アドレスのリストを維持する必要があります。プログラムの数とそのメモリ使用量は、予測できない方法で変化します。

これらのケースのいずれにおいても、(動的な挿入と削除に対応する) 可変サイズのリストが単純な配列よりも優れたパフォーマンスを発揮するという良い議論をすることができます。主な利点は、固定配列に要素を追加または削除するときに、固定配列のメモリ領域を割り当て/割り当て解除する必要性が減少することです。

于 2011-06-24T03:39:08.097 に答える