C++: 实战:通讯录系统
最后更新:2026-08-26
第50课我们学了单元测试。
现在,我们要做第一个综合实战项目——通讯录系统。
这个项目会综合运用前面学到的所有知识。
1. 项目需求
(1) 1.1 功能需求
| 功能 | 说明 |
|---|---|
| 添加联系人 | 输入姓名、电话、邮箱 |
| 删除联系人 | 按姓名或电话删除 |
| 修改联系人 | 修改信息 |
| 查找联系人 | 按姓名或电话查找 |
| 显示所有 | 列出所有联系人 |
| 保存/加载 | 持久化到文件 |
(2) 1.2 类的设计
TEXT
📖 仅展示
Contact(联系人)
- name, phone, email
AddressBook(通讯录)
- vectorContact contacts
- add, remove, update, find, display
- save, load
2. 联系人类
(1) 2.1 Contact类定义
CPP
#include <iostream>
#include <string>
#include <fstream>
class Contact {
private:
std::string name;
std::string phone;
std::string email;
public:
Contact() = default;
Contact(const std::string& name, const std::string& phone, const std::string& email)
: name(name), phone(phone), email(email) {}
// Getter
std::string getName() const { return name; }
std::string getPhone() const { return phone; }
std::string getEmail() const { return email; }
// Setter
void setName(const std::string& n) { name = n; }
void setPhone(const std::string& p) { phone = p; }
void setEmail(const std::string& e) { email = e; }
// 显示
void display() const {
std::cout << "姓名:" << name << std::endl;
std::cout << "电话:" << phone << std::endl;
std::cout << "邮箱:" << email << std::endl;
std::cout << "-------------------" << std::endl;
}
// 序列化(保存到文件)
void save(std::ofstream& file) const {
file << name << "," << phone << "," << email << std::endl;
}
// 反序列化(从文件加载)
static Contact load(std::ifstream& file) {
Contact c;
std::getline(file, c.name, ',');
std::getline(file, c.phone, ',');
std::getline(file, c.email);
return c;
}
};
3. 通讯录类
(1) 3.1 AddressBook类定义
▶ 示例 1:STL容器使用(难度⭐)
CPP
📖 仅展示
#include <vector>
#include <algorithm>
class AddressBook {
private:
std::vectorContact contacts;
public:
// 添加联系人
void addContact(const Contact& c) {
contacts.push_back(c);
std::cout << "添加成功!" << std::endl;
}
// 查找联系人(按姓名)
Contact* findByName(const std::string& name) {
for (auto& c : contacts) {
if (c.getName() == name) {
return &c;
}
}
return nullptr;
}
// 删除联系人(按姓名)
bool removeByName(const std::string& name) {
auto it = std::remove_if(contacts.begin(), contacts.end(),
[&name](const Contact& c) {
return c.getName() == name;
});
if (it != contacts.end()) {
contacts.erase(it, contacts.end());
std::cout << "删除成功!" << std::endl;
return true;
}
std::cout << "未找到联系人:" << name << std::endl;
return false;
}
// 显示所有联系人
void displayAll() const {
if (contacts.empty()) {
std::cout << "通讯录为空" << std::endl;
return;
}
for (const auto& c : contacts) {
c.display();
}
}
// 保存到文件
void saveToFile(const std::string& filename) const {
std::ofstream file(filename);
if (!file) {
std::cerr << "无法打开文件" << std::endl;
return;
}
for (const auto& c : contacts) {
c.save(file);
}
file.close();
std::cout << "保存成功!" << std::endl;
}
// 从文件加载
void loadFromFile(const std::string& filename) {
std::ifstream file(filename);
if (!file) {
std::cerr << "无法打开文件" << std::endl;
return;
}
contacts.clear(); // 清空现有数据
std::string line;
while (file.peek() != EOF) {
contacts.push_back(Contact::load(file));
}
file.close();
std::cout << "加载成功!" << std::endl;
}
};
输出:
TEXT
📖 仅展示
添加成功!
删除成功!
未找到联系人:
通讯录为空
保存成功!
加载成功!
4. 主菜单
(1) 4.1 菜单实现
CPP
void showMenu() {
### ▶ 示例 2:基础编程实践(难度⭐)
std::cout << "\n===== 通讯录管理系统 =====" << std::endl;
std::cout << "1. 添加联系人" << std::endl;
std::cout << "2. 删除联系人" << std::endl;
std::cout << "3. 查找联系人" << std::endl;
std::cout << "4. 显示所有联系人" << std::endl;
std::cout << "5. 保存" << std::endl;
std::cout << "6. 加载" << std::endl;
std::cout << "0. 退出" << std::endl;
std::cout << "==========================" << std::endl;
std::cout << "请选择:";
}
int main() {
AddressBook book;
int choice;
do {
showMenu();
std::cin >> choice;
switch (choice) {
case 1: {
std::string name, phone, email;
std::cout << "姓名:";
std::cin >> name;
std::cout << "电话:";
std::cin >> phone;
std::cout << "邮箱:";
std::cin >> email;
book.addContact(Contact(name, phone, email));
break;
}
case 2: {
std::string name;
std::cout << "姓名:";
std::cin >> name;
book.removeByName(name);
break;
}
case 3: {
std::string name;
std::cout << "姓名:";
std::cin >> name;
Contact* c = book.findByName(name);
if (c) {
c->display();
} else {
std::cout << "未找到" << std::endl;
}
break;
}
case 4:
book.displayAll();
break;
case 5: {
book.saveToFile("contacts.txt");
break;
}
case 6: {
book.loadFromFile("contacts.txt");
break;
}
case 0:
std::cout << "再见!" << std::endl;
break;
default:
std::cout << "无效选择" << std::endl;
}
} while (choice != 0);
return 0;
}
5. 扩展建议
(1) 5.1 功能扩展
| 功能 | 难度 |
|---|---|
| 按电话查找 | ⭐ |
| 修改联系人 | ⭐⭐ |
| 按姓名排序 | ⭐⭐ |
| 导入/导出CSV | ⭐⭐⭐ |
| 图形界面 | ⭐⭐⭐⭐ |
6. ❓ 练习
(1) 基础练习(难度⭐)
给通讯录添加"按电话查找"功能。
(2) 进阶练习(难度⭐⭐)
给通讯录添加"修改联系人"功能。
(3) 挑战练习(难度⭐⭐⭐)
用 std::map 重构通讯录,提高查找效率。
▶ 示例 3:联系人结构体定义(难度⭐)
CPP
#include <iostream>
#include <string>
struct Contact {
std::string name;
std::string phone;
std::string email;
void display() const {
std::cout << "姓名:" << name << std::endl;
std::cout << "电话:" << phone << std::endl;
std::cout << "邮箱:" << email << std::endl;
}
};
int main() {
Contact c1 = {"张三", "13800138000", "zhangsan@example.com"};
Contact c2 = {"李四", "13900139000", "lisi@example.com"};
c1.display();
std::cout << "---" << std::endl;
c2.display();
return 0;
}
输出:
TEXT
📖 仅展示
姓名:
电话:
邮箱:
---
❓ 常见问题
Q 为什么用vector而不是数组?
A vector会自动扩容,不需要预先知道元素个数。数组的大小必须在编译时确定,而vector可以在运行时灵活增减。
Q 为什么联系人信息用结构体而不是多个数组?
A 结构体把相关数据打包在一起,逻辑更清晰。如果用多个数组,增删操作容易出错(名字删了但电话忘了删)。
Q 文件读写时需要注意什么?
A ①打开文件后要检查是否成功(is_open);②写入后要close();③读取时循环判断eof()或good()避免死循环;④异常处理确保文件正确关闭。
📖 小节
| 知识点 | 应用 |
|---|---|
| 面向对象 | Contact和AddressBook类 |
| STL | vector存储联系人 |
| 文件操作 | 保存/加载 |
| 字符串处理 | 解析CSV |
- 通讯录系统:增删改查四大核心功能
- 用结构体存储联系人信息
- vector 容器管理动态数据
- 文件读写实现数据持久化
- 模块化函数封装各功能
📝 作业
-
基础题 (Difficulty ⭐): 运行通讯录程序,添加3个联系人(姓名、电话、分组),查看显示效果。尝试用"编辑"功能修改其中一个联系人的信息。
-
进阶题 (Difficulty ⭐⭐): 给通讯录增加"收藏联系人"功能——在结构体里加一个
bool isFavorite字段,在显示列表时把收藏联系人标为[★]。 -
挑战题 (Difficulty ⭐⭐⭐): 实现"模糊搜索"功能:用户输入关键字后,匹配所有姓名中含关键字的联系人并显示。如果输入为空则显示全部。支持不区分大小写匹配。
下一课:实战:简易数据库(#52)