C++で行列データやグリッド状の情報を扱う際、従来のC言語スタイルの配列(int arr[10][10])よりも、標準ライブラリの std::vector を使った2次元配列の方が圧倒的に便利で安全です。
vector の右に出るものはありません。この記事では、C++の vector を使った2次元配列の基本的な宣言・初期化方法から、push_back を使った要素の追加、動的なサイズ変更(resize)まで、実務で役立つテクニックをサンプルコード付きで徹底解説します。
![]() 執筆者:マヒロ |
|
vectorを使った2次元配列の基本構造
C++で vector を使って2次元配列を表現するには、「vectorの中にvectorを入れる」という入れ子構造(ネスト)を作ります。
型としては std::vector<std::vector<int>> のようになります。
記述が少し長くなりますが、柔軟性は抜群です。
宣言と初期化の基本パターン
まずは、最も基本的な宣言と初期化の方法を見ていきましょう。
サイズ(行数・列数)を指定して、初期値ですべて埋める方法です。
#include <iostream>
#include <vector>
int main() {
// 3行4列の2次元配列を宣言し、すべて0で初期化する
// 外側のvector(行)のサイズを3、内側のvector(列)のサイズを4に設定
int rows = 3;
int cols = 4;
std::vector<std::vector<int>> matrix(rows, std::vector<int>(cols, 0));
// 値を出力して確認
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
std::cout << matrix[i][j] << " ";
}
std::cout << std::endl;
}
return 0;
}
実行結果
0 0 0 0
0 0 0 0
0 0 0 0
std::vector<std::vector<int>> matrix が2次元配列の宣言部分です。
コンストラクタの引数として (rows, std::vector<int>(cols, 0)) を渡しています。
これは、「std::vector<int>(cols, 0)(サイズ4で中身が0の1次元配列)」を、「rows(3つ)分」作成して初期化するという意味になります。
これにより、3行4列のゼロ埋めされた行列が一発で生成されます。
初期化子リストを使った宣言
C++11以降では、初期化子リストを使って、宣言と同時に具体的な値を代入することも可能です。
テストデータを作成する際などに非常に便利です。
#include <iostream>
#include <vector>
int main() {
// 初期化子リストを使って値を設定
std::vector<std::vector<int>> matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 範囲for文(range-based for)で出力
// const auto& を使うことで無駄なコピーを防ぐ(Best Practice)
for (const auto& row : matrix) {
for (int val : row) {
std::cout << val << " ";
}
std::cout << std::endl;
}
}
実行結果
1 2 3
4 5 6
7 8 9
波括弧 {} を入れ子にすることで、直感的に2次元配列を定義できます。
また、出力部分では const auto& を使った範囲for文を利用しています。
これにより、添字(インデックス)を使わずにスマートに全要素へアクセスできるだけでなく、要素のコピーコストを防ぐことができるため、パフォーマンス面でも優れた書き方です。
2次元配列への要素の追加(push_back)
vector の最大の強みは、サイズを後から変更できる点です。
push_back メソッドを使うことで、新しい行や、行内の新しい要素を簡単に追加できます。
新しい「行」を追加する
既存の2次元配列に、新しい行(1次元配列)を追加するパターンです。
#include <iostream>
#include <vector>
int main() {
// 空の2次元配列
std::vector<std::vector<int>> matrix;
// 追加する行データ
std::vector<int> row1 = {1, 2, 3};
std::vector<int> row2 = {4, 5, 6};
// push_backで2次元配列に行を追加
matrix.push_back(row1);
matrix.push_back(row2);
// 直接初期化リストを追加することも可能
matrix.push_back({7, 8, 9});
// 出力
for (const auto& row : matrix) {
for (int val : row) {
std::cout << val << " ";
}
std::cout << std::endl;
}
}
実行結果
1 2 3
4 5 6
7 8 9
matrix.push_back(row1) のように、vector<int> 型のデータを push_back することで、2次元配列の新しい行として追加されます。
空の状態からデータを積み上げていく場合によく使われる手法です。
push_back({7, 8, 9}) のように値を直接渡すことも可能です。
既存の行に「要素(列)」を追加する
すでにある行に対して、さらにデータを追加したい場合は、内側の vector にアクセスしてから push_back します。
#include <iostream>
#include <vector>
int main() {
std::vector<std::vector<int>> matrix = {
{1, 2},
{3, 4}
};
// 0行目の末尾に要素を追加
matrix[0].push_back(100);
// 1行目の末尾に要素を追加
matrix[1].push_back(200);
// 出力
for (const auto& row : matrix) {
for (int val : row) {
std::cout << val << " ";
}
std::cout << std::endl;
}
}
実行結果
1 2 100
3 4 200
matrix[0] で「0行目の1次元配列」にアクセスできます。
これに対して push_back(100) を呼ぶことで、その行の末尾に値が追加されます。
この方法を使えば、行ごとに長さが異なるジャグ配列(ギザギザ配列)を作ることも可能です。
動的確保とサイズ変更(resize)
あらかじめサイズが決まっていない場合や、処理の途中でサイズを変更したい場合は resize メソッドを使用します。
宣言時ではなく、後から領域を動的に確保したい場合に有効です。
行数と列数を動的に変更する
#include <iostream>
#include <vector>
int main() {
int H, W;
std::cout << "行数と列数を入力してください: ";
std::cin >> H >> W;
// H行のvectorを作成(中身は空)
std::vector<std::vector<int>> matrix(H);
// 各行をW列にリサイズし、値を入力
for (int i = 0; i < H; ++i) {
matrix[i].resize(W); // i行目のサイズをWにする
// 値を代入(例としてすべて1にする)
for (int j = 0; j < W; ++j) {
matrix[i][j] = 1;
}
}
// サイズ確認
std::cout << "行数: " << matrix.size() << std::endl;
std::cout << "列数(0行目): " << matrix[0].size() << std::endl;
}
まず std::vector<std::vector<int>> matrix(H); で、空の行をH個作成します。
その後、ループを使って matrix[i].resize(W); を実行し、各行の列数をW個に拡張しています。
このコードは「後からサイズが決まる」場合に有効ですが、もし最初からサイズが判明している場合は、ループを使わずに以下のように宣言時に一括で確保する方が効率的です(メモリの再確保が発生しないため)。 std::vector<std::vector<int>> matrix(H, std::vector<int>(W));
vectorの2次元配列を使うメリットと注意点
便利な vector ですが、従来の配列と比較してどのような利点や注意点があるのでしょうか。
特性を理解して使い分けることが重要です。
メリット
- メモリ管理が楽:
newやdeleteによる手動のメモリ管理が不要で、メモリリークのリスクがありません。 - 柔軟性: サイズを自由に変更でき、行ごとに異なる長さを持たせることも可能です。
- 標準ライブラリの恩恵: ソートや検索など、STLのアルゴリズムをそのまま適用できます。
注意点:アクセス速度とメモリ配置
- メモリの断片化:
vector<vector<int>>は、外側のベクタが「各行(内側のベクタ)へのポインタ」を持っている構造です。そのため、各行の実データはメモリ上のバラバラな場所に確保される可能性があります。 - キャッシュ効率: メモリが連続していないため、CPUのキャッシュ効率が悪くなり、大規模な数値計算や画像処理など、極限のパフォーマンスが求められる場面では、C言語の固定長配列(
int arr[][])や後述する1次元化手法に比べて速度が劣ることがあります。
【上級者向けコラム】さらに高速化したい場合
画像処理や科学技術計算など、パフォーマンスを最優先したい場合は、2次元配列を「1次元の vector」として確保し、計算で2次元的にアクセスする手法(フラット化)が推奨されます。
// H行W列のデータを1次元配列で確保
std::vector<int> data(H * W);
// (y, x) の要素へのアクセス
// index = y * W + x という計算式を使う
int value = data[y * W + x];
この方法であれば、データがメモリ上で完全に連続することが保証されるため、キャッシュ効率が最大化され、アクセス速度が大幅に向上します。
C++のスキルを活かして年収を上げる方法
以上、C++で、vectorで2次元配列を作る方法について解説してきました。
なお、C++のスキルがある場合には、「転職して年収をアップさせる」「副業で稼ぐ」といった方法を検討するのがおすすめです。
C++を扱えるエンジニアは希少価値が高いため、転職によって数十万円の年収アップはザラで、100万円以上年収が上がることも珍しくありません。
なお、転職によって年収を上げたい場合は、エンジニア専門の転職エージェントサービスを利用するのが最適です。
併せて、副業案件を獲得できるエージェントにも登録しておくと、空いている時間を活かして稼げるようなC++の案件を探しやすくなります。
転職エージェントも副業エージェントも、登録・利用は完全無料なので、どんな求人や副業案件があるのか気になる方は、気軽に利用してみるとよいでしょう。



