ベクトル(動的配列)
サイズが可変の配列。標準ライブラリの vector を学ぶ
概要
通常の配列は、サイズが固定です。 しかし実際のアプリケーションでは、 データが増えたり減ったりするため、 動的にサイズを変更できる配列が必要です。
その役割を果たすのがstd::vectorです。 これは「動的配列」とも呼ばれます。
ベクトルの基本
宣言と初期化
#include <iostream>
#include <vector>
int main() {
std::vector<int> empty; // 空のベクトル
std::vector<int> with_size(5); // 5つの要素(全て0)
std::vector<int> initialized = {1, 2, 3, 4, 5}; // 初期化
for (int x : initialized) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
1 2 3 4 5
要素の追加(push_back)
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers;
numbers.push_back(10);
numbers.push_back(20);
numbers.push_back(30);
std::cout << "サイズ: " << numbers.size() << std::endl;
for (int x : numbers) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
サイズ: 3
10 20 30
要素へのアクセス
std::vector<int> v = {10, 20, 30};
std::cout << v[0] << std::endl; // 10
std::cout << v.at(1) << std::endl; // 20(at()は範囲チェック付き)
std::cout << v.front() << std::endl; // 10(最初の要素)
std::cout << v.back() << std::endl; // 30(最後の要素)
ベクトルと配列の比較
| 特性 | 配列 | std::vector |
|---|---|---|
| サイズ | 固定 | 可変 |
| 宣言時 | int arr[10] | std::vector<int> v |
| 要素追加 | 不可 | push_back() |
| 要素削除 | 不可 | pop_back() |
| メモリ管理 | スタック | ヒープ(自動管理) |
| パフォーマンス | 若干高速 | 若干低速(メリット大) |
ベクトルの操作
サイズとキャパシティ
#include <iostream>
#include <vector>
int main() {
std::vector<int> v;
std::cout << "初期 - Size: " << v.size() << ", Capacity: " << v.capacity() << std::endl;
v.push_back(1);
v.push_back(2);
v.push_back(3);
std::cout << "追加後 - Size: " << v.size() << ", Capacity: " << v.capacity() << std::endl;
return 0;
}
初期 - Size: 0, Capacity: 0
追加後 - Size: 3, Capacity: 4
size() は実際の要素数、 capacity()は確保されたメモリ量です。
要素の削除と挿入
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {10, 20, 30, 40};
v.pop_back(); // 最後の要素を削除
v.insert(v.begin() + 1, 15); // インデックス1に15を挿入
v.erase(v.begin() + 2); // インデックス2を削除
for (int x : v) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
10 15 20
実践例
例1: スコア管理アプリ
#include <iostream>
#include <vector>
int main() {
std::vector<int> scores;
// スコアを入力
int score;
while (true) {
std::cout << "スコア(終了:-1): ";
std::cin >> score;
if (score == -1) break;
scores.push_back(score);
}
// 統計情報を表示
int total = 0;
for (int s : scores) {
total += s;
}
std::cout << "個数: " << scores.size() << std::endl;
std::cout << "合計: " << total << std::endl;
std::cout << "平均: " << (total / (double)scores.size()) << std::endl;
return 0;
}
例2: 素数フィルター
#include <iostream>
#include <vector>
bool isPrime(int n) {
if (n < 2) return false;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
return true;
}
int main() {
std::vector<int> numbers = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
std::vector<int> primes;
for (int n : numbers) {
if (isPrime(n)) {
primes.push_back(n);
}
}
std::cout << "素数: ";
for (int p : primes) {
std::cout << p << " ";
}
std::cout << std::endl;
return 0;
}
素数: 2 3 5 7 11
2次元ベクトル
2次元配列のようにベクトルのベクトルを使えます。
#include <iostream>
#include <vector>
int main() {
std::vector<std::vector<int>> matrix;
// 3行のベクトルを作成
for (int i = 0; i < 3; i++) {
std::vector<int> row;
for (int j = 0; j < 3; j++) {
row.push_back(i * 3 + j + 1);
}
matrix.push_back(row);
}
// 表示
for (auto & row : matrix) {
for (int val : row) {
std::cout << val << " ";
}
std::cout << std::endl;
}
return 0;
}
1 2 3
4 5 6
7 8 9
ポイント
- std::vectorは動的配列の標準
- push_back()で要素を追加、pop_back()で削除
- size()とcapacity()の違いを理解
- 配列より使いやすく、メモリも自動管理
- 2次元ベクトルも簡単に作成可能
よくある誤り
誤り1: 空のベクトルにアクセス
std::vector<int> v;
std::cout << v[0] << std::endl; // 危険!: アクセス違反
誤り2: at()と[]の混同
std::vector<int> v = {1, 2, 3};
std::cout << v[10] << std::endl; // 未定義の動作
std::cout << v.at(10) << std::endl; // 例外発生(より安全)
誤り3: イテレータの無効化
std::vector<int> v = {1, 2, 3};
auto it = v.begin();
v.push_back(4); // メモリ再配置の可能性、イテレータが無効に
// *it はもう使えない
やってみよう
練習1: ユーザーから複数の整数を入力し、合計を計算。
練習2: ベクトルを反転するプログラム。
練習3: 2つのベクトルをマージするプログラム。
チャレンジ: ベクトルを昇順にソートするプログラム(std::sort を使用)。
まとめ
- std::vectorは modern C++での標準コンテナ
- 配列の制限を克服し、動的にサイズを変更可能
- メモリ管理が自動で行われるため、安全
- 大規模データのアプリケーションで必須