HOWTO · C++
C++ STL でベクトルの配列を宣言する
この記事では、C++ でベクトルの配列を宣言する方法を紹介します
このページの内容
この記事では、C++ でベクトルの行数を固定する方法と、行数を変更できるベクトルのベクトルを宣言する方法を説明します。外側のコンテナーで追加できる行数が決まり、各内側の std::vector でその行に格納できる値の数が決まります。行数が固定なら前者、外側のコレクションを増減させる必要があるなら後者を選びます。
固定配列とベクトルのベクトルを使い分ける
ベクトルの配列では行数がコンパイル時に決まりますが、各行はそれぞれ独立して要素を増やせるベクトルです。一方、ベクトルのベクトルでは行数も変更でき、各行の長さも異なる場合があります。そのため、ベクトルのベクトルが必ずしも長方形の行列になるとは限りません。各項目が異なる数の隣接項目を持つ隣接リストなどのデータに適しています。
どちらの形式でも、要素として格納された内側のベクトルがそれぞれのメモリを管理します。どちらの宣言も、すべての行に同じ数の値が入ることを保証しません。アルゴリズムで長方形の形状が必要なら、各行を同じ幅で初期化し、データを変更するときもその条件を保ってください。
C スタイルの配列表記でベクトルの配列を宣言する
行数を固定するには、要素型を std::vector<int> とする配列を宣言します。たとえば、std::vector<int> rows[3]; と書くと、ベクトル要素が3つある配列になります。これらのベクトルは最初は値を持ちませんが、rows[0].push_back(10) のようにして1行に値を追加できます。配列自体は4つ目のベクトルを格納できるようには拡張されません。行数を変更する必要がある場合は、外側に可変サイズのコンテナーを使ってください。
この構文は、行数がプログラムの固定構造の一部である場合に便利です。配列のサイズは宣言時に決まりますが、各ベクトルのサイズはプログラムの実行中に変えられます。つまり、2つの次元には異なる規則が適用されます。外側の配列は固定で、内側のベクトルはサイズを変更できます。
サイズ変更可能なベクトルのベクトルを宣言する
行数を変更できるようにするには、std::vector<std::vector<int>> を使います。初期化リストを使えば、最初の長さが異なる行を作れます。別の方法として、std::vector<std::vector<int>> rows(3, std::vector<int>(2, 0)); と書くと、ゼロを2つずつ含む行が3つ作られます。このコンストラクター形式では、最初の引数が行数で、2番目の引数として渡すベクトルが各行の初期化にコピーされます。
rows.push_back({8, 9}) で新しい行を追加したり、rows[0].push_back(7) で既存の行に値を追加したりできます。ある行のサイズを増減しても、ほかの行のサイズは自動的には変わりません。最初に規則的な格子状のデータを作るならコンストラクター形式を、行ごとに長さが異なるなら初期化リストか行ごとの構築を選びます。
ベクトルのベクトルにアクセスして反復処理を行う
インデックスが有効だと分かっている場合は、rows[row][column] で直接アクセスできます。それぞれの添字は別のベクトルに適用されます。最初の添字で外側の行を選び、次の添字でその行の値を選びます。行ごとに長さが異なることがあるため、ある行で使える列インデックスが、別の行でも使えるとは限りません。
境界を検査しながらアクセスするには rows.at(row).at(column) を使います。at() は選択したベクトルの範囲外を黙って参照する代わりに、範囲外アクセスを報告します。行の長さが等しいと仮定せずにすべての値を調べるには、範囲ベースのループを入れ子にします。格納された要素を変更する場合は内側の値を参照(int& value)で受け取ります。読み取りだけなら値または const 参照を使えます。以下の完成例では、両方の宣言形式、添字によるアクセス、境界検査付きアクセス、値の追加、参照による値の変更を示します。
この完成プログラムでは、2種類のコンテナーを使い、格納された値にアクセスします。
このセクションのコードと表示されるラベルや出力は英語のままです。
#include <iostream>
#include <vector>
int main() {
std::vector<int> fixed_rows[3] = {{1, 2}, {3}, {4, 5, 6}};
std::vector<std::vector<int>> rows{{1, 2}, {3}, {4, 5, 6}};
fixed_rows[0].push_back(7);
rows.push_back({8, 9});
rows[1].push_back(7);
rows.at(0).at(1) = 20;
std::cout << fixed_rows[0][2] << '\n';
std::cout << rows.at(0).at(1) << '\n';
for (auto& row : rows) {
for (int& value : row) {
value *= 2;
std::cout << value << ' ';
}
std::cout << '\n';
}
}