C++: プロジェクト:簡易データベース
最終更新:2026-08-31
レッスン51では、住所録システムを作りました。
ここでは、総合実践プロジェクト——簡易データベースを作ります。
このプロジェクトはC++の知識を統合し、学習成果を示します。
1. プロジェクト概要
(1) 1.1 機能一覧
| 機能 | 説明 |
|---|---|
| CREATE TABLE | テーブル作成 |
| INSERT | データ挿入 |
| SELECT | データ検索 |
| WHERE | 条件指定 |
| 保存 | ファイルへ保存 |
| トランザクション | ACIDトランザクション |
(2) 1.2 設計方針
プロジェクトをシンプルにするため、以下の制約を設けます:
intとstring型のみ- インメモリデータベース
- インデックスは
std::mapで実装 - 永続化(ファイル保存)
2. データ構造
(1) 2.1 定義
▶ サンプル 2:コード例(難易度 ⭐)
TEXT
📖 参照専用
#include <iostream>
#include <string>
#include <variant>
#include <vector>
// 列定義
struct Column {
std::string name;
enum Type { INT, STRING } type;
};
// 値(variant)
using Value = std::variant<int, std::string>;
// 行(値の配列)
using Row = std::vector<Value>;
// テーブル
class Table {
private:
std::string name;
std::vector<Column> columns;
std::vector<Row> rows;
public:
Table(const std::string& name, const std::vector<Column>& columns)
: name(name), columns(columns) {}
// 挿入
void insert(const Row& row) {
if (row.size() != columns.size()) {
std::cerr << "列数が一致しません..." << std::endl;
return;
}
rows.push_back(row);
}
// 全件検索
std::vector<Row> select() {
return rows;
}
// 構造表示
void describe() const {
std::cout << "テーブル:" << name << std::endl;
std::cout << "列:" << std::endl;
for (const auto& col : columns) {
std::cout << " " << col.name << " (";
if (col.type == Column::INT) {
std::cout << "INT";
} else {
std::cout << "STRING";
}
std::cout << ")" << std::endl;
}
}
};
3. WHERE条件の実装
(1) 3.1 述語の使用
CPP
// 条件関数(WHERE)
using Predicate = std::function<bool(const Row&)>;
// 条件検索
std::vector<Row> selectWhere(Predicate pred) {
std::vector<Row> result;
for (const auto& row : rows) {
if (pred(row)) {
result.push_back(row);
}
}
return result;
}
// 使用例:age > 18
auto predicate = [](const Row& row) {
int age = std::get<int>(row[1]); // 2列目がage
return age > 18;
};
auto result = table.selectWhere(predicate);
4. インデックスの実装
(1) 4.1 インデックス
TEXT
📖 参照専用
#include <map>
// インデックス(int型)
class Index {
private:
std::map<int, std::vector<int>> index; // 値 -> 行番号
public:
void build(const std::vector<Row>& rows, int colIndex) {
index.clear();
for (int i = 0; i < rows.size(); i++) {
int key = std::get<int>(rows[i][colIndex]);
index[key].push_back(i);
}
}
std::vector<int> lookup(int key) {
auto it = index.find(key);
if (it != index.end()) {
return it->second;
}
return {};
}
};
5. トランザクションの実装
(1) 5.1 トランザクション
CPP
#include <stack>
class Transaction {
private:
std::stack<std::vector<Row>> undoStack;
public:
// 開始(スナップショット)
void begin(Table& table) {
undoStack.push(table.rows);
}
// コミット(確定)
void commit() {
while (!undoStack.empty()) {
undoStack.pop();
}
}
// ロールバック(取り消し)
void rollback(Table& table) {
if (!undoStack.empty()) {
table.rows = undoStack.top();
undoStack.pop();
}
}
};
6. 使用例
▶ サンプル 1:データベースの使用(難易度 ⭐⭐⭐)
TEXT
📖 参照専用
int main() {
// テーブル作成
Table t("users", {{"name", Column::STRING}, {"age", Column::INT}});
// データ挿入
t.insert({"田中", 25});
t.insert({"佐藤", 30});
t.insert({"鈴木", 20});
// 全件検索
auto rows = t.select();
std::cout << "検索結果:" << std::endl;
for (const auto& row : rows) {
std::cout << std::get<std::string>(row[0]) << ", "
<< std::get<int>(row[1]) << std::endl;
}
return 0;
}
7. 拡張機能
(1) 7.1 機能一覧
| 機能 | 難易度 | 説明 |
|---|---|---|
| DELETE | ⭐⭐ | ファイル保存 |
| JOIN | ⭐⭐⭐⭐ | テーブル結合 |
| SQLパーサー | ⭐⭐⭐⭐⭐ | SQL文法解析 |
| 並行性制御 | ⭐⭐⭐⭐ | マルチスレッド対応 |
8. 練習問題
(1) 初級(難易度 ⭐⭐)
データベースに DELETE 機能を追加。
(2) 中級(難易度 ⭐⭐⭐)
データベースに UPDATE 機能を追加。
(3) 上級(難易度 ⭐⭐⭐⭐)
簡易SQLパーサーを実装し、SELECT * FROM table WHERE age > 18 を解析。
▶ サンプル 3:簡易テーブルクエリ(難易度 ⭐⭐)
CPP
#include <iostream>
#include <vector>
#include <string>
#include <functional>
struct Row {
std::string name;
int age;
};
void queryWhere(std::vector<Row>& rows, std::function<bool(const Row&)> pred) {
for (const auto& row : rows) {
if (pred(row)) {
std::cout << row.name << ", " << row.age << std::endl;
}
}
}
int main() {
std::vector<Row> users = {
{"田中", 25},
{"佐藤", 17},
{"鈴木", 30},
{"山田", 16}
};
std::cout << "成年人 (age >= 18):" << std::endl;
queryWhere(users, [](const Row& r) { return r.age >= 18; });
return 0;
}
出力:
TEXT 📖 参照専用成年人 (age >= 18): 田中, 25 鈴木, 30
❓ よくある質問
Q:データベースプロジェクトで最も重要なことは? A:まず核心概念を理解し、実践例で練習することです。
Q:データベースを効果的に練習するには? A:シンプルな例から始め、徐々に複雑さを増やし、常にコードをテストしてください。
Q:よくある間違いは? A:コンパイラのエラーメッセージを注意深く読み、常に変数を初期化してから使用してください。
📖 まとめ
| トピック | 応用 |
|---|---|
| variant | 複数型の値 |
vector<Row> |
テーブル実装 |
| map | インデックス実装 |
| 関数オブジェクト | WHERE条件実装 |
- データベース:インメモリ + ファイル永続化
- mapでインデックスを実装しO(log n)の検索
- variantで型安全な値表現
- CSVやJSONでのシリアライズ
- 例外処理でプログラムを堅牢に
📝 練習問題
-
初級(難易度 ⭐): データベースプログラムを拡張し、新規テーブルを作成(列:名前、電話、年齢)、3件のデータを挿入し、全件検索。
-
中級(難易度 ⭐⭐): 「ソート」機能を追加——
SELECT * FROM table ORDER BY 年齢をサポート。ヒント:std::sort+ ラムダで年齢順ソート。 -
上級(難易度 ⭐⭐⭐): 「結合」機能を実装——2つのテーブルを結合(INNER JOIN)、
SELECT t1.名前, t2.年齢 FROM t1 JOIN t2 ON t1.id = t2.id。JOINのロジックを設計・実装。
次のレッスン:コースまとめと次のステップ(#53)