C++のベクトルについて

AI実装検定のご案内

C++には複数のデータをまとめて扱うための方法がいくつか存在します。

その中でも特に重要なのが、標準ライブラリに含まれる vector(ベクトル) というコンテナです。

vectorは、要素数を自由に増減できる 可変長配列(dynamic array) を実装したデータ構造です。

C++では固定サイズの配列も使用できますが、サイズを後から変更することができません。

一方、vectorは必要に応じて要素を追加したり削除したりできるため、柔軟にデータを管理できます。

このような理由から、現代のC++では可変長の配列が必要な場面では、従来の配列よりもvectorが使われることが多くなっています。

目次

vectorの基本的な特徴

vectorの最大の特徴は、要素数を自由に変更できることです。

要素が追加されると内部的にサイズが増え、削除されるとサイズが減ります。

またvectorは、要素を 連続したメモリ領域 に格納するという性質を持っています。この特徴により、次のような利点があります。

  • 任意の位置の要素へ高速にアクセスできる
  • 先頭から順番にデータを処理する際の効率が良い
  • キャッシュ効率が高い

このため、配列と同じような感覚で扱えるうえ、より柔軟に利用できるデータ構造として広く使われています。

vectorの初期状態

vectorを作成した直後は、通常は要素が存在しない空の状態になります。

必要に応じて要素を追加することで、vectorのサイズは自動的に増えていきます。

またvectorは、あらかじめ一定数の要素を持つ形で作成することもできます。

この場合、指定した数だけ要素が用意され、それぞれの要素は 値初期化 という方法で初期化されます。

例えば整数型の場合、値初期化された要素の値は0になります。

ただしこれは整数型の性質によるものであり、型によって初期値の扱いは異なります。

さらにvectorでは、初期値を指定して要素をまとめて作成することも可能です。

この方法を使うと、すべての要素を同じ値で初期化できます。

要素の追加

vectorには要素を追加するための仕組みが用意されています。

一般的には、vectorの末尾に新しい要素を追加する操作が最もよく使われます。

この操作によって、vectorのサイズは1つ増加します。

ただしvectorには内部的な容量の概念があり、その容量を超えて要素を追加すると 再配置(reallocation) が発生します。

再配置が起こると、次のような処理が行われます。

  1. より大きなメモリ領域を新たに確保する
  2. 既存の要素を新しい領域へコピーまたはムーブする
  3. 古いメモリ領域を解放する

この処理によってvectorはより多くの要素を格納できるようになります。

ただし再配置が発生すると、以前のメモリ領域に依存していた ポインタ、参照、イテレータ が無効化される可能性があります。

この点はvectorを使う際の重要な注意点です。

要素の削除

vectorでは要素を削除することも可能です。

もっとも簡単な削除方法は、末尾の要素を取り除く操作です。

この操作ではvectorのサイズが1つ減少します。

また、特定の位置にある要素を削除することもできます。

ただしvectorは連続したメモリを使って要素を管理しているため、途中の要素を削除すると、その後ろにある要素を前へ詰める必要があります。

このため、末尾以外の削除には 線形時間(O(n)) の処理コストがかかります。

さらに、削除された位置以降の要素に対するイテレータや参照は無効化されることがあります。

要素へのアクセス

vectorの要素には、配列と同じようにインデックスを使ってアクセスできます。

この方法は非常に高速ですが、範囲チェックは行われません。

もし存在しない位置にアクセスしてしまうと、プログラムの動作は未定義になります。

一方、vectorには範囲チェックを行う安全なアクセス方法も用意されています。

この方法では、範囲外アクセスが行われた場合に例外が発生します。

そのため、安全性を重視する場合にはこちらの方法を使うこともあります。

sizeとcapacityの違い

vectorには「サイズ」と「容量」という2つの概念があります。

サイズは、現在vectorに格納されている要素数を表します。

容量は、再配置を行わずに格納できる最大要素数を意味します。

例えば、現在3つの要素が格納されていて容量が4の場合、もう1つ要素を追加しても再配置は発生しません。

しかしそれ以上の要素を追加すると、vectorはより大きなメモリ領域を確保する必要があります。

容量の事前確保

vectorでは、あらかじめ一定量の容量を確保しておくことができます。

この操作は、大量の要素を追加することが分かっている場合に有効です。

事前に容量を確保しておくことで、再配置が発生する回数を減らし、処理性能を改善できる場合があります。

ただしこの操作は、必ずしもすべての再配置を完全に防ぐわけではありません。

ループ処理

vectorの要素は、さまざまな方法で順番に処理できます。

もっとも基本的な方法は、インデックスを使って先頭から順番に要素を取り出す方法です。

また、C++11以降では範囲ベースのループ構文を使って、より簡潔にすべての要素を処理することもできます。

さらに、イテレータという仕組みを使う方法もあります。

イテレータはコンテナ内の要素を順番に指し示すオブジェクトで、STLアルゴリズムと組み合わせて使われることが多いです。

2次元vector

vectorは入れ子構造にすることで、多次元のデータを扱うことができます。

例えば、vectorの中に別のvectorを格納することで、表形式のデータを表現することが可能です。

ただしこの構造は、あくまでvectorの入れ子であり、C言語の多次元配列とはメモリ配置が異なります。

各行のvectorは独立したメモリ領域を持つため、完全に連続したメモリ構造になるわけではありません。

vectorの性能特性

vectorは多くの場面で効率的に動作しますが、すべての操作が高速というわけではありません。

vectorが得意とする操作には次のようなものがあります。

  • 任意位置へのランダムアクセス
  • 末尾への要素追加
  • 先頭から順番にデータを処理する操作

一方で、次のような操作はあまり得意ではありません。

  • 先頭への挿入
  • 途中への挿入
  • 途中の要素削除

これらの操作では要素を移動させる必要があるため、処理コストが大きくなります。

vectorを使うメリット

vectorが広く使われている理由は次の通りです。

  • メモリ管理を自動で行う
  • 要素数を自由に変更できる
  • ランダムアクセスが高速
  • STLアルゴリズムと相性が良い
  • 連続メモリによる高いキャッシュ効率

これらの特徴により、vectorはC++で最も基本的かつ重要なコンテナの一つとされています。

vectorを使う際の注意点

vectorを使用する際には、いくつか注意しておくべきポイントがあります。

まず、途中挿入や途中削除は処理コストが高くなります。

また、容量不足によって再配置が発生すると、イテレータや参照が無効化されることがあります。

さらに、インデックスによるアクセスは範囲チェックを行わないため、誤った位置にアクセスすると未定義動作になる可能性があります。

これらの特性を理解したうえで使用することが重要です。

まとめ

vectorは、C++標準ライブラリに含まれる可変長配列コンテナです。

主な特徴は次の通りです。

  • 要素数を自由に増減できる
  • 要素は連続したメモリに格納される
  • ランダムアクセスが高速
  • STLアルゴリズムと組み合わせて使いやすい

以上、C++のベクトルについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次