C++: STL コンテナ
これまで、特定の型の関数・クラスを書くテンプレートを学びました。
でも実際のプロジェクトでは、コンテナ(動的配列、連結リスト)を自分で書くのは大変。
C++標準ライブラリのテンプレート(STL)は既製のコンテナを提供、すぐに使える!
1. STL とは?
STL(Standard Template Library、標準テンプレートライブラリ)は C++ 標準ライブラリの中核:
| 構成要素 | 役割 | 例 |
|---|---|---|
| コンテナ | データを格納 | std::vector、std::array |
| イテレータ | コンテナの要素にアクセス | begin()、end() |
| アルゴリズム | データを操作 | std::sort、std::find |
| 関数オブジェクト | 関数を定義 | std::less、std::greater |
2. vector(最もよく使う)
(1) 2.1 vector とは?
std::vector は動的配列 —— 要素を追加・削除すると自動的にサイズが変わる。
| 特徴 | 配列 | vector |
|---|---|---|
| サイズ変更 | 不可 | 可(動的) |
| メモリ | スタックまたはヒープ | ヒープ |
| アクセス | arr[i] |
vec[i] または vec.at(i) |
| 使いやすさ | ⭐⭐ | ⭐⭐⭐⭐⭐ |
▶ サンプル 1:vector の基本的な使い方(難易度 ⭐)
#include <iostream>
#include <vector>
int main() {
// int の vector
std::vector<int> vec = {1, 2, 3, 4, 5};
// 要素へのアクセス
std::cout << "最初の要素:" << vec[0] << std::endl;
std::cout << "2番目の要素:" << vec.at(1) << std::endl;
// 要素の変更
vec[0] = 100;
// サイズの取得
std::cout << "サイズ:" << vec.size() << std::endl;
return 0;
}
vec.at(i) は境界チェックし、範囲外なら例外を投げる;vec[i] はチェックなし、高速。
(2) 2.2 追加と削除
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec;
// 末尾に追加
vec.push_back(10);
vec.push_back(20);
vec.push_back(30);
// 末尾を削除
vec.pop_back();
// 中間に挿入
vec.insert(vec.begin() + 1, 15); // 位置1に15を挿入
// 中間を削除
vec.erase(vec.begin() + 1); // 位置1を削除
return 0;
}
(3) 2.3 vector の走査
方法1:インデックス(昔ながらの方法)
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (size_t i = 0; i < vec.size(); i++) {
std::cout << vec[i] << " ";
}
std::cout << std::endl;
return 0;
}
方法2:範囲for文(C++11以降、推奨)
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (int x : vec) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
方法3:イテレータ(一番柔軟)
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
3. array(固定サイズ配列)
(1) 3.1 array とは?
std::array は固定サイズの配列、でも従来の配列より使いやすい。
| 特徴 | 従来の配列 | array |
|---|---|---|
| サイズ取得 | 不可 | 可(size() で) |
| ポインタへの変換 | 自動 | 明示的 |
| 使いやすさ | ⭐⭐ | ⭐⭐⭐⭐ |
▶ サンプル 2:array の基本的な使い方(難易度 ⭐)
#include <iostream>
#include <array>
int main() {
// 5個の int の array
std::array<int, 5> arr = {1, 2, 3, 4, 5};
// 要素へのアクセス
std::cout << "最初の要素:" << arr[0] << std::endl;
// サイズの取得
std::cout << "サイズ:" << arr.size() << std::endl;
return 0;
}
std::array のサイズはコンパイル時に決定、動的に変えられない。
4. deque(両端キュー)
(1) 4.1 deque とは?
std::deque は両端キュー —— 先頭と末尾の両方で高速に追加・削除。
| 操作 | vector | deque |
|---|---|---|
| 末尾追加 | O(1) | O(1) |
| 先頭追加 | O(n) | O(1) |
| ランダムアクセス | O(1) | O(1) |
▶ サンプル 3:deque の基本的な使い方(難易度 ⭐⭐)
#include <iostream>
#include <deque>
int main() {
std::deque<int> dq;
// 末尾に追加
dq.push_back(10);
dq.push_back(20);
// 先頭に追加
dq.push_front(5);
dq.push_front(1);
// dqの内容:1, 5, 10, 20
for (int x : dq) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
5. list(連結リスト)
(1) 5.1 list とは?
std::list は連結リスト —— 各要素が次の要素のアドレスを持つ。
| 操作 | vector | list |
|---|---|---|
| ランダムアクセス | O(1) | ❌ 不可 |
| 挿入・削除 | O(n) | O(1) |
| メモリ使用 | 連続 | 追加(ポインタ) |
▶ サンプル 4:list の基本的な使い方(難易度 ⭐⭐)
#include <iostream>
#include <list>
int main() {
std::list<int> lst = {1, 2, 3, 4, 5};
// 先頭に追加
lst.push_front(0);
// 末尾に追加
lst.push_back(6);
// 値で削除(3を削除)
lst.remove(3);
for (int x : lst) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
6. forward_list(片方向連結リスト)
(1) 6.1 forward_list とは?
std::forward_list は片方向連結リスト —— 各要素が次の要素のアドレスだけを持つ。
| 特徴 | list | forward_list |
|---|---|---|
| メモリ使用 | 多い | 少ない |
| 逆方向走査? | ✅ 可 | ❌ 不可 |
| 使いやすさ | ⭐⭐⭐⭐ | ⭐⭐(上級者向け) |
std::list を使う。
7. コンテナの選び方
| 使用場面 | 推奨コンテナ |
|---|---|
| 動的配列 | std::vector |
| 固定サイズ、C配列より安全 | std::array |
| 先頭・末尾の高速追加・削除 | std::deque |
| 中間の高速挿入・削除 | std::list |
| メモリ節約、片方向で十分 | std::forward_list |
std::vector を使う。特別な要件がある場合だけ他のコンテナを検討。
8. 実践例:vector で動的配列を実装(難易度 ⭐⭐)
#include <iostream>
#include <vector>
#include <string>
struct Student {
std::string name;
int age;
double score;
};
int main() {
std::vector<Student> students;
// 学生を追加
students.push_back({"Alice", 20, 92.5});
students.push_back({"Bob", 21, 88.0});
students.push_back({"Charlie", 19, 95.0});
// 出力
for (const auto& s : students) {
std::cout << "名前:" << s.name
<< "、年齢:" << s.age
<< "、点数:" << s.score << std::endl;
}
return 0;
}
❓ よくある質問
Q: vector と array の違いは? A:
vectorは動的、サイズ可変、ヒープメモリarrayは固定サイズ、スタックメモリサイズが変わる場合、
vectorを使う;サイズが固定なら、arrayを使う。
Q: vector で範囲for文を使うべき? A: はい。より簡潔で、ループ条件を書く必要がない。
CPP// 昔の書き方(条件を書く) for (size_t i = 0; i < vec.size(); i++) { ... } // 範囲for文(簡潔、推奨) for (int x : vec) { ... }
Q: list と vector の使い分けは? A: 使い分け:
- ランダムアクセスが多い(
vec[100])、vectorが高速- 挿入・削除が多い、
listが高速
📖 まとめ
- STL コンテナは C++ 標準ライブラリのデータ構造
- vector は一番よく使う(動的配列)
- array は固定サイズの配列
- deque は先頭・末尾の高速操作
- list は連結リスト、挿入・削除が高速
- 基本は vector、特別な要件があれば他を検討
📝 練習問題
-
初級(難易度 ⭐):
std::vector<int>に5つの整数を追加し、出力。 -
中級(難易度 ⭐⭐):
std::vector<std::string>に3つの文字列をユーザー入力で追加し、出力。 -
上級(難易度 ⭐⭐⭐):
std::dequeで「回文判定」を実装:- 文字を先頭と末尾から比較
- 例:
"racecar"は回文、"hello"は違う
9. 🚀 次のステップ
STL コンテナを理解したら、次は STL アルゴリズム(レッスン35) —— 標準ライブラリのアルゴリズムでコンテナを操作、ソートや検索を自分で書かなくていい……!