C++: 实战:简易数据库
最后更新:2026-08-26
第51课我们做了通讯录系统。
现在,我们要做第二个综合实战项目——简易数据库。
这个项目会用到C++的很多高级特性,是检验学习成果的试金石。
1. 项目需求
(1) 1.1 功能需求
| 功能 | 说明 |
|---|---|
| CREATE TABLE | 创建表 |
| INSERT | 插入数据 |
| SELECT | 查询数据 |
| WHERE | 条件过滤 |
| 索引 | 加速查询 |
| 事务 | 保证ACID |
(2) 1.2 简化设计
由于是教学项目,我们做简化版:
- 只支持
int和string类型 - 只支持单表查询
- 索引用
std::map实现 - 数据保存在内存中(可选持久化)
2. 表结构设计
(1) 2.1 列定义
▶ 示例 2:代码示例(难度⭐)
CPP
#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::vectorValue;
// 表
class Table {
private:
std::string name;
std::vectorColumn columns;
std::vectorRow rows;
public:
Table(const std::string& name, const std::vectorColumn& 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::vectorRow 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;
}
}
};
输出:
TEXT
📖 仅展示
表名:
列:
(
INT
STRING
)
3. 查询实现
(1) 3.1 WHERE子句
CPP
// 谓词函数(用于WHERE)
using Predicate = std::function<bool(const Row&)>;
// 带条件的查询
std::vectorRow selectWhere(Predicate pred) {
std::vectorRow 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::getint(row[1]); // 假设第2列是age
return age > 18;
};
auto result = table.selectWhere(predicate);
4. 索引实现
(1) 4.1 简单索引
CPP
#include <map>
// 索引(简化版:只对int类型建索引)
class Index {
private:
std::map<int, std::vector<int>> index; // 值 -> 行号列表
public:
void build(const std::vectorRow& rows, int colIndex) {
index.clear();
for (int i = 0; i < rows.size(); i++) {
int key = std::getint(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::vectorRow> undoStack;
public:
// 开始事务(保存当前状态)
void begin(Table& table) {
undoStack.push(table.rows);
}
// 提交(清空undo栈)
void commit() {
while (!undoStack.empty()) {
undoStack.pop();
}
}
// 回滚(恢复上一个状态)
void rollback(Table& table) {
if (!undoStack.empty()) {
table.rows = undoStack.top();
undoStack.pop();
}
}
};
6. 完整示例
▶ 示例 1:使用简易数据库(难度⭐⭐⭐)
CPP
int main() {
// 创建表
Table t("users", {{"name", Column::STRING}, {"age", Column::INT}});
// 插入数据
t.insert({"Alice", 25});
t.insert({"Bob", 30});
t.insert({"Charlie", 20});
// 查询所有
auto rows = t.select();
std::cout << "所有用户:" << std::endl;
for (const auto& row : rows) {
std::cout << std::getstd::string(row[0]) << ", "
<< std::getint(row[1]) << std::endl;
}
return 0;
}
输出:
TEXT
📖 仅展示
所有用户:
,
7. 扩展方向
(1) 7.1 功能扩展
| 功能 | 难度 | 说明 |
|---|---|---|
| 持久化 | ⭐⭐ | 保存到文件 |
| JOIN | ⭐⭐⭐⭐ | 多表连接 |
| SQL解析器 | ⭐⭐⭐⭐⭐ | 解析SQL语句 |
| 并发控制 | ⭐⭐⭐⭐ | 多线程安全 |
8. ❓ 练习
(1) 基础练习(难度⭐⭐)
给数据库添加DELETE功能。
(2) 进阶练习(难度⭐⭐⭐)
给数据库添加UPDATE功能。
(3) 挑战练习(难度⭐⭐⭐⭐)
实现简单的SQL解析器,支持SELECT * FROM table WHERE age > 18。
▶ 示例 3:数据库表结构定义(难度⭐)
CPP
#include <iostream>
#include <string>
#include <vector>
#include <variant>
// 值类型(支持多种数据类型)
using Value = std::variant<int, std::string, double>;
// 一行数据
struct Row {
int id;
Value name;
Value age;
Value score;
void display() const {
std::cout << "ID: " << id << std::endl;
std::cout << "Name: " << std::get<std::string>(name) << std::endl;
std::cout << "Age: " << std::get<int>(age) << std::endl;
std::cout << "Score: " << std::get<double>(score) << std::endl;
}
};
int main() {
Row r = {1, std::string("Alice"), 20, 95.5};
r.display();
return 0;
}
输出:
TEXT
📖 仅展示
ID:
Name:
Age:
Score:
❓ 常见问题
Q map的查找效率有多快?
A map内部是红黑树实现,查找、插入、删除都是O(log n)时间复杂度。对于100万条数据,最坏情况下只需约20次比较。
Q 为什么用CSV存储文件?
A CSV格式简单、可读性好、可以用Excel直接打开查看。JSON格式支持嵌套结构但解析稍微复杂。生产环境推荐用SQLite。
Q 如何防止SQL注入?
A 本项目是简易实现,实际生产环境一定要使用参数化查询或预处理语句。永远不要直接拼接用户输入的字符串到SQL语句中。
📖 小节
| 知识点 | 应用 |
|---|---|
| variant | 存储不同类型的值 |
| vectorRow | 存储表数据 |
| map | 实现索引 |
| 函数对象 | 实现WHERE谓词 |
- 简易数据库:内存数据 + 文件持久化
- 用 map 实现 O(log n) 的键查找
- 支持增删改查命令接口
- CSV 或 JSON 格式存储数据
- 异常处理确保程序稳定性
📝 作业
-
基础题 (Difficulty ⭐): 运行数据库程序,创建一张"学生表"(字段:学号、姓名、年龄、成绩),插入3条记录,查询所有记录。
-
进阶题 (Difficulty ⭐⭐): 增加"排序查询"功能——支持
SELECT * FROM table ORDER BY 年龄。提示:用std::sort+ lambda 表达式按指定字段排序。 -
挑战题 (Difficulty ⭐⭐⭐): 实现"多表关联"——创建两张表(学生表、成绩表),支持
SELECT 学生.姓名, 成绩.分数 FROM 学生 JOIN 成绩 ON 学生.学号 = 成绩.学号。需要设计 JOIN 逻辑和结果合并。
下一课:课程总结与进阶路线(#53)