这学期 C++ 课设要求做一个完整的桌面应用。我选择做一个通讯录管理系统, 从命令行版本做起,逐步加入了多态架构、文件持久化、CSV 导入导出等功能。 这篇文章记录整个设计和实现过程。
需求梳理
在动手之前,先把核心需求列清楚:
- 支持不同类型的联系人:同学、同事、朋友、亲戚,每种有各自的特有字段
- 增删改查基本操作
- 按名称模糊搜索、按分类查看
- 按姓名或生日排序
- 生日提醒(未来 5 天内)
- 每月生日人数统计
- 数据持久化存储(程序关了数据不丢)
- CSV 导入导出,方便迁移数据
- 控制台界面,彩色输出
类体系设计 — 多态是核心
如果只是存姓名和电话,一个 struct 就够了。但不同联系人类型有不同字段——
同学有"学校",同事有"公司",亲戚有"关系"——这就需要用继承 + 多态来设计。
// 抽象基类
class Person {
protected:
string name, birthDate, phone, email, customGroup;
public:
virtual void display() const = 0; // 纯虚函数
virtual void saveToStream(ostream&) const = 0;
};
// 派生类
class Classmate : public Person {
string school; // 同学特有字段
};
class Colleague : public Person {
string company; // 同事特有字段
};
class Friend : public Person {
string hobby; // 朋友特有字段
};
class Relative : public Person {
string relationship; // 亲戚特有字段
};
基类
Person 持有所有类型的共性问题(姓名、电话等),
派生类各自扩展特有问题(学校、公司等)。
display() 和 saveToStream() 声明为纯虚函数,
强制每个派生类实现自己的展示和存储逻辑——这就是多态的核心。
数据存储 — 文件持久化
不能让数据在程序关闭后消失。最简单的持久化方案就是按类型分文件存储: 每种联系人类型各自一个文本文件,格式统一。
// 文件格式(以 Classmate 为例)
// classmates.txt
张三 | 2003-05-12 | 13800138000 | zhang@mail.com | 深圳大学 | 同学群
李四 | 2002-11-08 | 13900139000 | li@mail.com | 清华大学 | 项目组
用管道符 | 分隔字段,简单直接。
启动时自动读取所有文件,退出时自动保存——用户完全感知不到文件操作。
CSV 导入导出 — 打通外部数据
如果只能手动录入,这个系统的实用性就大打折扣。 加了 CSV 导入导出后,可以从 Excel 导出的通讯录直接导入,也可以把数据导出去备份。
void AddressBook::exportToCSV(const string& filename) const {
ofstream f(filename);
f << "类型,姓名,生日,电话,邮箱,分组,特有信息\n";
for (auto* p : contacts) {
f << p->getCategory() << ",";
f << p->getName() << ",";
f << p->getBirthDate() << ",";
// ... 各字段
p->saveToStream(f); // 多态调用
}
}
功能模块一览
整个系统共有 13 个功能选项:
- 添加联系人 — 选择类型,逐字段输入,自动校验格式
- 删除联系人 — 按姓名精确删除
- 修改联系人 — 找到后逐字段修改
- 搜索联系人 — 支持模糊匹配,输入"张"能找到所有张姓联系人
- 显示全部 — 列表形式展示所有联系人
- 按分类显示 — 分别展示同学、同事、朋友、亲戚
- 按姓名排序 — 升序排列
- 按生日排序 — 按月份和日期排序,方便看谁快过生日了
- 生日提醒 — 自动检测未来 5 天内过生日的联系人,支持发邮件祝福
- 月度统计 — 统计每个月有多少人生日
- 按分组显示 — 支持自定义分组标签(如"项目组"、"跑友群")
- 导出 CSV — 一键备份所有数据
- 导入 CSV — 批量导入,自动识别联系人类型
控制台美化 — 彩色界面
纯黑白控制台太单调了,加了一个简单的颜色工具:
// ConsoleUtils.h
void colorPrint(int color, const string& text) {
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
cout << text;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
}
菜单边框用青色绘制,选项用黄色,错误提示用红色——虽然只是控制台程序, 但视觉上的区分能大大提升使用体验。
遇到的坑
坑 1:内存管理
用 vector<Person*> 存联系人指针,必须在析构时手动释放所有对象。
最早忘了写 AddressBook 的析构函数,导致内存泄漏。
Valgrind 跑一遍才发现漏了几十个对象。
坑 2:CSV 中文字段
Windows 下导出的 CSV 用 GBK 编码,而我的程序默认用 UTF-8 写入。 导致导入 Excel 导出的 CSV 时中文全是乱码。解决方法是在读取时检测 BOM 头并做编码转换。
坑 3:cin 缓冲区残留
混用 cin >> 和 getline() 时,缓冲区里的换行符经常捣乱,
导致 getline 读到空字符串。解决方案是每次 cin >> 后
加一句 cin.ignore() 清掉缓冲。
这个项目让我真正理解了面向对象的三大特性——封装、继承、多态,不是背概念,而是在写代码的过程中自然体会到的。
后续改进方向
一、图形化界面(GUI)方案
命令行虽然高效,但对普通用户不够友好。将系统迁移到图形界面,有三种主流选择:
方案 A:Qt(C++ 原生,推荐)
Qt 是 C++ 最成熟的 GUI 框架,天然适合这个项目——不需要改业务逻辑,只替换 UI 层。
// 用 Qt 的 Model/View 架构重构
class ContactTableModel : public QAbstractTableModel {
AddressBook* book; // 复用现有逻辑
public:
int rowCount(...) const { return book->getSize(); }
QVariant data(...) const { /* 从 Person 读取字段 */ }
};
// 主窗口
class MainWindow : public QMainWindow {
QTableView* tableView;
ContactTableModel* model;
// 工具栏:添加、删除、搜索、导出...
};
- 优点:性能好、原生控件、跨平台(Windows/Linux/macOS)
- 难点:Qt 库较大(~50MB)、需要学习信号槽机制
- 适合:想深入 C++ GUI 开发
方案 B:Electron + Web 前端
用 HTML/CSS/JS 做界面,C++ 编译成后端服务或 Node.js 插件。界面漂亮且开发快。
- 优点:界面可以做得非常好看、组件生态丰富
- 难点:需要学 Node.js、进程通信、内存占用较大
- 适合:想做出像微信桌面版那样的效果
方案 C:imgui(轻量级)
Dear ImGui 是一个即时模式 GUI 库,几行代码就能出界面,适合工具类软件。
- 优点:超轻量、入门快、调试友好
- 难点:风格有限,不适合做漂亮的商业软件
先上 Qt —— 用 Qt Designer 拖出主界面,用 TableView 展示联系人列表, 用 Form 做编辑弹窗。现有的
AddressBook 和 Person 类可以完整复用,
只在 UI 层做替换。预计 2-3 天能出第一版。
二、数据层升级
SQLite 替代文本文件
当前每个联系人类型一个 txt 文件,查询和搜索效率低。换成 SQLite:
// SQLite 表设计
CREATE TABLE contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL, -- 'classmate'/'colleague'/'friend'/'relative'
name TEXT NOT NULL,
birthdate TEXT,
phone TEXT,
email TEXT,
group_tag TEXT,
extra TEXT -- JSON: 存每种类型的特有字段
);
// 搜索变得简单
SELECT * FROM contacts WHERE name LIKE '%张%' ORDER BY name;
- 好处:支持复杂查询、全文搜索、事务保证数据安全
- 额外收益:为后续的 Web API 和同步功能打好基础
三、功能扩展
1. 通讯录合并与去重
用户可能从手机、微信、QQ、Excel 多个来源导入联系人,难免有重复。实现智能去重:
- 同名 + 同电话 → 自动合并,提示用户确认
- 同名 + 不同电话 → 标记为"疑似重复",手动处理
- 合并时合并分组标签,不丢失任何信息
2. 标签系统
当前只有单一"分组"字段。升级为多标签体系:一个联系人可以有多个标签——如 #大学 #羽毛球 #前同事,支持按标签筛选和统计。
3. 生日提醒升级
- 当前:命令行提醒未来 5 天
- 升级:支持微信/邮件自动发送祝福(集成邮件模板)
- GUI 版:系统托盘通知 + 桌面弹窗
- 日历订阅:生成
.ics文件,可以导入手机日历
4. 名片识别
拍一张名片,OCR 识别姓名、电话、公司、邮箱,自动填充到通讯录中。可以用 Tesseract OCR 或百度 OCR API。
5. 数据可视化面板
- 联系人地域分布热力图
- 月度新增联系人趋势图
- 生日月份分布柱状图
- 各类型联系人占比饼图
Qt 有 QtCharts 模块可以原生实现,也可以导出数据用前端图表库展示。
四、网络同步
1. WebDAV / 云盘同步
把 SQLite 数据库文件存到坚果云、OneDrive 等同步盘,多台电脑自动同步。实现简单,零成本。
2. 自建同步服务器
用 Go 或 Python 写一个简单的 REST API 服务:
GET /api/contacts # 获取全部联系人
POST /api/contacts # 新增联系人
PUT /api/contacts/:id # 更新联系人
DELETE /api/contacts/:id # 删除联系人
POST /api/sync # 增量同步(传时间戳,只返回变更)
客户端每次启动时拉取最新数据,修改后推送到服务器。
3. 手机端配套 App
用 Flutter 做手机端,通过同步服务器与桌面端共享同一份通讯录。Flutter 一套代码同时出 Android 和 iOS。
五、工程化改进
- 单元测试:用 Google Test 给 AddressBook 的核心方法写测试用例
- CMake 构建:替代 VS 的 .sln 文件,跨平台构建更标准
- CI/CD:GitHub Actions 自动编译 + 运行测试 + 打包发布
- 国际化:支持中英文界面切换(Qt 自带 i18n 支持)
- 暗色模式:Qt 6.5+ 原生支持暗色主题
"从命令行到 GUI,从文本文件到数据库,从单机到多端同步——这就是一个课程作业进化成真正产品的路线图。每一步都是技术跃迁。"
如果你也在学 C++,强烈建议动手做一个小项目而不是只看教程。 写这个通讯录的过程比我刷 100 道语法题学到的都多。选择合适的下一步,继续打磨它。