C ++でのstd :: vectorとstd :: arrayの比較


283

C ++でのstd::vectorとの違いは何std::arrayですか?いつ他の人よりも好まれるべきですか?それぞれの長所と短所は何ですか?私の教科書がしていることは、それらがどのように同じであるかをリストすることだけです。


1
私はの比較のために探していますstd::vectorstd::arrayとどのように用語が異なっています。
Zud

Zud std::arrayはC ++配列と同じではありません。std::arrayクラスのユーザーからポインターを隠すことを主な目的とする、C ++配列の非常に薄いラッパーです。回答を更新します。
ClosureCowboy 2010

説明を反映するため、質問のタイトルとテキストを更新しました。
Matteo Italia

回答:


318

std::vector動的配列1をカプセル化するテンプレートクラスです。ヒープに格納され、要素が追加または削除されると自動的に拡大および縮小します。これは、すべてのフック(提供begin()end()それはSTLの残りの部分と罰金を動作させる、イテレータ、など)。また、ベクトルの中央に要素を挿入するなど、通常の配列では扱いにくい操作を実行できるいくつかの便利なメソッドもあります(これは、背後にある次の要素を移動するすべての作業を処理します)。

ヒープに割り当てられたメモリに要素を格納するため、静的配列に関してオーバーヘッドが発生します。

std::array静的サイズの配列をカプセル化するテンプレートクラスであり、オブジェクト自体の内部に格納されます。つまり、スタックでクラスをインスタンス化すると、配列自体がスタックに配置されます。そのサイズはコンパイル時に既知である必要があり(テンプレートパラメーターとして渡されます)、拡大または縮小できません。

はより制限されていますがstd::vector、実際にはほとんどがCスタイルの配列の軽量ラッパーであるため、特に小さいサイズの場合は、多くの場合より効率的です。ただし、ポインターへの暗黙の変換が無効になっているため、より安全であり、std::vectorSTLアルゴリズムおよびその他のコンテナーでSTL関連の機能の多くを提供するため、STLアルゴリズムおよびcoで簡単に使用できます。とにかく、固定サイズの非常に制限があるため、ほど柔軟ではありませんstd::vector

概要についてはstd::array、見ていこの記事をstd::vector可能な操作の簡単な紹介については、そのドキュメントをご覧ください。


  1. 実際、私は標準では、さまざまな操作の最大の複雑さ(例:一定時間でのランダムアクセス、線形時間でのすべての要素の反復、一定の償却時間での要素の追加と削除)に関して記述されていると思います。など)、しかし私の知る限り、動的配列を使用する以外にそのような要件を満たす方法はありません。@Lucretielにより述べたように、標準は、実際に、要素が連続して格納されている必要があり、それが関連するアロケータプットその格納されたダイナミックアレイ。

6
あなたの脚注に関して:trueである一方で、標準は内部要素でのポインタ演算が機能することも保証します。つまり、配列である必要があります。&vec [9]-&vec [3] == 6はtrueです。
Lucretiel

9
そのベクトルは自動的には縮小されませんが、C ++ 11以降では、shrink_to_fitを呼び出すことができます。
Dino

静的配列という用語に完全に混乱し、正しい用語が何であるかわかりません。静的なサイズの配列であり、静的な変数の配列ではありません(静的なストレージを使用する配列)。stackoverflow.com/questions/2672085/…。正しい用語は何ですか?静的配列は、固定サイズの配列のずさんな用語ですか?
Zボソン

3
@Zboson:それは間違いなくあなただけではありません、静的はかなり乱用された用語です。非常にstaticC ++でのキーワードは、3つの異なる無関係な意味を持っており、この用語はまた、コンパイル時に固定されているものについての話に頻繁に使用されています。「静的サイズ」がもう少し明確であることを願っています。
Matteo Italia

3
もう一つ注意すべき:(あなたが持っていることになっていないリアルタイムのプログラミングのための任意の起動後の動的割り当て/割り当て解除は)はstd ::配列は、おそらくのstd ::ベクトルよりも優先されるだろう。
TED

17

std::vector<T>クラスの使用:

  • ... 組み込み配列を使用して実行できること(既存の要素の読み取りと書き込み)のみを実行している場合、組み込み配列を使用するのと同じくらい高速です。

  • ...新しい要素が挿入されると自動的にサイズ変更されます。

  • ... ベクトルの最初または途中に新しい要素を挿入して、残りの要素を自動的に「上」にシフトすることができます(意味がありますか?)。また、内の任意の場所にある要素を削除してstd::vector、残りの要素を自動的に下に移動することもできます。

  • ...メソッドを使用して範囲チェック済みの読み取りを実行できますat()[]このチェックを実行したくない場合は、常にインデクサーを使用できます)。

使用には2つの 3つの主な注意事項がありますstd::vector<T>

  1. 基になるポインターへの信頼できるアクセスがありません。配列のアドレスを要求するサードパーティの関数を処理している場合は、問題になる可能性があります。

  2. std::vector<bool>クラスは愚かです。配列ではなく、圧縮されたビットフィールドとして実装されます。boolsの配列が必要な場合は避けてください。

  3. 使用中、std::vector<T>sは同じ数の要素を持つC ++配列よりも少し大きくなります。これは、現在のサイズなど、他の少量の情報を追跡する必要があるためstd::vector<T>です。また、sのサイズを変更するたびに、必要以上のスペースを確保するためです。これは、新しい要素が挿入されるたびにサイズを変更する必要がないようにするためです。この動作はカスタムを提供することで変更できますが、allocatorその必要性を感じたことは一度もありません!


編集:質問に対するZudの回答を読んだ後、これを追加する必要があると感じました:

std::array<T>クラスは、C ++の配列と同じではありません。std::array<T>は、C ++配列の非常に薄いラッパーであり、クラスのユーザーからポインターを隠すことを主な目的としています(C ++では、配列は暗黙的にポインターとして暗黙的にキャストされ、多くの場合、期待を裏切る結果になります)。std::array<T>クラスはまた、その大きさ(長さ)は、非常に有用であり得る記憶します。


5
動的に割り当てられた組み込み配列を使用するのと同じくらい高速です。一方、自動配列を使用すると、パフォーマンスが大幅に異なる可能性があります(局所性の影響により、割り当て時だけでなく)
Ben Voigt

4
C ++ 11以降の非ブールベクトルの場合はdata()、a std::vector<T>を呼び出して、基になるポインターを取得できます。要素0のアドレスを取得することもできます(C ++ 11での動作が保証されていますが、おそらく以前のバージョンでも動作します)。
Matt

16

@MatteoItaliaによって指摘された点を強調するために、効率の違いはデータが格納される場所です。ヒープメモリ(で必要vector)は、システムにメモリを割り当てるための呼び出しを必要とします。これは、サイクルをカウントしている場合にはコストがかかる可能性があります。スタックメモリ(の可能性がありますarray)は、メモリがスタックポインタを調整するだけで割り当てられ、関数へのエントリ時に1回だけ実行されるため、実質的に時間の観点から「ゼロオーバーヘッド」です。スタックは、メモリの断片化も回避します。確かに、std::array常にスタックにあるとは限りません。割り当てる場所によって異なりますが、ヒープとのメモリ割り当ては、ベクターに比べて1つ少なくなります。あなたが持っている場合

  • 小さな「配列」(100要素未満と言う)-(通常のスタックは約8MBなので、コードが再帰的である場合は、スタックに数KB以上を割り当てないでください)
  • サイズは固定されます
  • 存続期間は関数スコープ内にあります(または親クラスと同じ存続期間を持つメンバー値です)
  • あなたはサイクルを数えています、

間違いなくstd::arrayベクトルを使用します。これらの要件のいずれかが真でない場合は、を使用しstd::vectorます。


3
素敵な答え。「確かに、std :: arrayは常にスタック上にあるとは限りません。それは、どこに割り当てるかによって異なります。」では、要素数の多いスタック上にないstd :: arrayを作成するにはどうすればよいでしょうか。
Trilarion 2017年

4
@Trilarionを使用するnew std::arrayか、 'new`を使用して割り当てるクラスのメンバーにします。
マークラカタ2017年

つまり、これはnew std::array、コンパイル時にそのサイズを知っていることを期待しており、サイズを変更することはできませんが、ヒープ上に残っているということですか?
Trilarion 2017年

はい。new std::arrayvs を使用しても大きなメリットはありませんnew std::vector
マークラカタ2017年

12

多次元配列の使用を検討している場合は、std :: arrayとstd :: vectorの間に1つの追加の違いがあります。多次元std :: arrayは、cスタイルの配列と同じように、すべての次元で要素をメモリにパックします。多次元std :: vectorは、すべての次元でパックされるわけではありません。

次の宣言があるとします。

int cConc[3][5];
std::array<std::array<int, 5>, 3> aConc;
int **ptrConc;      // initialized to [3][5] via new and destructed via delete
std::vector<std::vector<int>> vConc;    // initialized to [3][5]

cスタイルの配列(cConc)またはstd :: array(aConc)の最初の要素へのポインターは、先行する各要素に1を追加することにより、配列全体を反復できます。彼らはしっかりと詰め込まれています。

ベクトル配列(vConc)の最初の要素へのポインターまたはポインター配列(ptrConc)は、最初の5つの要素(この場合)を介してのみ反復可能であり、(システムでは)12バイトのオーバーヘッドがあります。次のベクトル。

つまり、[3] [1000]配列として初期化されたstd :: vector>配列は、[1000] [3]配列として初期化された配列よりもメモリ内ではるかに小さくなり、両方ともstdよりもメモリ内で大きくなります。いずれかの方法で割り当てられた配列。

これは、メモリのオーバーヘッドを考慮せずに単純に多次元ベクトル(またはポインター)配列をopenGLに渡すことはできないが、単純に多次元std :: arrayをopenGLに渡して機能させることもできることを意味します。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.