C++课设:通讯录管理系统(vector、map协作实现)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)
专栏介绍:《编程项目实战》
目录
- 一、为什么选择C++开发通讯录系统?
- 1. C++的现状
- 2. STL标准模板库的威力
- 二、系统架构设计与STL容器选型
- 1. 三层架构设计
- 2. STL容器选型的智慧
- 3. 系统架构图解
- 三、完整代码及功能详解
- 1. 完整代码
- 2. 核心功能详解
- 四、STL容器的巧妙运用
- 1. vector:动态数组的艺术
- 2. map:红黑树的高效查找
- 3. 双容器协作的设计模式
- 4. STL算法的强大助力
- 五、用户体验与界面优化
- 1. 美观的界面设计
- 2. 智能的错误处理
- 3. 数据展示的艺术
- 六、总结与建议
- 1. 项目亮点总结
- 2. 性能优化建议
- 3. 学习收获与启发
在移动互联网时代,通讯录管理系统看似简单,但背后蕴含着丰富的数据结构与算法知识。本文将带你从零构建一个现代化的C++通讯录系统,重点探索STL容器的巧妙运用,让你的代码既高效又优雅!
一、为什么选择C++开发通讯录系统?
在这个Python、JavaScript满天飞的时代,为什么我们还要用C++来开发通讯录系统呢?答案很简单:因为它依然是性能之王!
1. C++的现状
根据最新的JetBrains开发者调查报告显示,C++作为一门有40多年历史的"老将",依然在关键战场上发光发热。特别是在嵌入式系统、游戏开发、金融科技等对性能要求极高的领域,C++始终占据着不可替代的地位。
2. STL标准模板库的威力
STL(Standard Template Library) 是C++的核心优势之一。STL提供了一套功能强大的模板类和函数集合,包括容器、算法、迭代器等六大组件,让我们可以:
- 高效管理数据:
vector
、map
等容器经过高度优化 - 简化开发流程:不用重复造轮子,专注业务逻辑
- 保证代码质量:经过无数项目验证的稳定性
二、系统架构设计与STL容器选型
想象一下,你要设计一座图书馆,你需要考虑:书籍怎么存放?如何快速找到某本书?同样,设计通讯录系统也需要巧妙的架构。
1. 三层架构设计
我们的系统采用经典的三层架构:
- 用户界面层:负责与用户交互,处理输入输出
- 业务逻辑层:核心功能实现,包含
AddressBook
和Contact
类 - 数据存储层:基于STL容器的高效数据管理
2. STL容器选型的智慧
这里是最关键的设计决策!我们选择了双剑合璧的方案:
1️⃣vector<Contact>
- 主存储容器
vector<Contact> contacts; // 存储所有联系人
为什么选择vector?
- 连续内存存储,访问速度快如闪电
- 动态扩容,不用担心容量限制
- 支持随机访问,可以直接通过下标操作
2️⃣map<string, int>
- 快速索引
map<string, int> nameIndex; // 姓名索引
map<string, int> phoneIndex; // 电话索引
为什么用map做索引?
- 红黑树实现,查找复杂度O(log n)
- 自动排序,key值始终有序
- 高效插入删除,维护成本低
3. 系统架构图解
通过下面的架构图,你可以直观地看到各个组件是如何协作的:
三、完整代码及功能详解
现在进入最激动人心的部分——看看这些功能是如何一步步实现的!
1. 完整代码
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <iomanip>
#include <cctype>using namespace std;// 联系人类
class Contact {
private:string name; // 姓名string phone; // 电话号码string email; // 邮箱string address; // 地址public:// 构造函数Contact() {}Contact(const string& n, const string& p, const string& e = "", const string& a = "") : name(n), phone(p), email(e), address(a) {}// 获取器string getName() const { return name; }string getPhone() const { return phone; }string getEmail() const { return email; }string getAddress() const { return address; }// 设置器void setName(const string& n) { name = n; }void setPhone(const string& p) { phone = p; }void setEmail(const string& e) { email = e; }void setAddress(const string& a) { address = a; }// 显示联系人信息void display() const {cout << left << setw(15) << name << setw(15) << phone << setw(25) << email << setw(30) << address << endl;}// 详细显示void displayDetailed() const {cout << "姓名: " << name << endl;cout << "电话: " << phone << endl;cout << "邮箱: " << email << endl;cout << "地址: " << address << endl;cout << string(40, '-') << endl;}
};// 通讯录管理类
class AddressBook {
private:vector<Contact> contacts; // 存储所有联系人map<string, int> nameIndex; // 姓名索引map<string, int> phoneIndex; // 电话索引// 更新索引void updateIndexes() {nameIndex.clear();phoneIndex.clear();for (size_t i = 0; i < contacts.size(); ++i) {nameIndex[contacts[i].getName()] = i;phoneIndex[contacts[i].getPhone()] = i;}}// 字符串转小写(用于不区分大小写的搜索)string toLowerCase(const string& str) const {string result = str;transform(result.begin(), result.end(), result.begin(), ::tolower);return result;}public:// 添加联系人bool addContact(const Contact& contact) {// 检查是否已存在相同姓名或电话if (nameIndex.find(contact.getName()) != nameIndex.end()) {cout << "错误: 姓名 '" << contact.getName() << "' 已存在!" << endl;return false;}if (phoneIndex.find(contact.getPhone()) != phoneIndex.end()) {cout << "错误: 电话号码 '" << contact.getPhone() << "' 已存在!" << endl;return false;}contacts.push_back(contact);updateIndexes();cout << "联系人添加成功!" << endl;return true;}// 根据姓名查找联系人Contact* findByName(const string& name) {map<string, int>::iterator it = nameIndex.find(name);if (it != nameIndex.end()) {return &contacts[it->second];}return NULL;}// 根据电话查找联系人Contact* findByPhone(const string& phone) {map<string, int>::iterator it = phoneIndex.find(phone);if (it != phoneIndex.end()) {return &contacts[it->second];}return NULL;}// 模糊搜索(按姓名)vector<Contact*> searchByName(const string& keyword) {vector<Contact*> results;string lowerKeyword = toLowerCase(keyword);for (size_t i = 0; i < contacts.size(); ++i) {string lowerName = toLowerCase(contacts[i].getName());if (lowerName.find(lowerKeyword) != string::npos) {results.push_back(&contacts[i]);}}return results;}// 删除联系人(根据姓名)bool deleteByName(const string& name) {map<string, int>::iterator it = nameIndex.find(name);if (it != nameIndex.end()) {contacts.erase(contacts.begin() + it->second);updateIndexes();cout << "联系人删除成功!" << endl;return true;}cout << "未找到姓名为 '" << name << "' 的联系人!" << endl;return false;}// 删除联系人(根据电话)bool deleteByPhone(const string& phone) {map<string, int>::iterator it = phoneIndex.find(phone);if (it != phoneIndex.end()) {contacts.erase(contacts.begin() + it->second);updateIndexes();cout << "联系人删除成功!" << endl;return true;}cout << "未找到电话为 '" << phone << "' 的联系人!" << endl;return false;}// 按姓名排序void sortByName() {sort(contacts.begin(), contacts.end(), NameComparator());updateIndexes();cout << "已按姓名排序!" << endl;}// 按电话排序void sortByPhone() {sort(contacts.begin(), contacts.end(), PhoneComparator());updateIndexes();cout << "已按电话号码排序!" << endl;}// 显示所有联系人void displayAll() const {if (contacts.empty()) {cout << "通讯录为空!" << endl;return;}cout << "\n" << string(85, '=') << endl;cout << " 通讯录列表" << endl;cout << string(85, '=') << endl;cout << left << setw(15) << "姓名" << setw(15) << "电话" << setw(25) << "邮箱" << setw(30) << "地址" << endl;cout << string(85, '-') << endl;for (size_t i = 0; i < contacts.size(); ++i) {contacts[i].display();}cout << string(85, '=') << endl;cout << "总计: " << contacts.size() << " 个联系人" << endl;}// 获取联系人数量int getContactCount() const {return contacts.size();}// 比较器类(用于排序)struct NameComparator {bool operator()(const Contact& a, const Contact& b) const {return a.getName() < b.getName();}};struct PhoneComparator {bool operator()(const Contact& a, const Contact& b) const {return a.getPhone() < b.getPhone();}};
};// 显示主菜单
void showMenu() {cout << "\n" << string(50, '=') << endl;cout << " 通讯录管理系统" << endl;cout << string(50, '=') << endl;cout << "1. 添加联系人" << endl;cout << "2. 查找联系人" << endl;cout << "3. 删除联系人" << endl;cout << "4. 显示所有联系人" << endl;cout << "5. 按姓名排序" << endl;cout << "6. 按电话排序" << endl;cout << "7. 模糊搜索" << endl;cout << "0. 退出系统" << endl;cout << string(50, '-') << endl;cout << "请选择操作 (0-7): ";
}// 添加联系人
void addContactMenu(AddressBook& book) {string name, phone, email, address;cout << "\n=== 添加联系人 ===" << endl;cout << "请输入姓名: ";cin.ignore();getline(cin, name);cout << "请输入电话: ";getline(cin, phone);cout << "请输入邮箱 (可选): ";getline(cin, email);cout << "请输入地址 (可选): ";getline(cin, address);Contact newContact(name, phone, email, address);book.addContact(newContact);
}// 查找联系人菜单
void searchContactMenu(AddressBook& book) {int choice;cout << "\n=== 查找联系人 ===" << endl;cout << "1. 按姓名查找" << endl;cout << "2. 按电话查找" << endl;cout << "请选择查找方式: ";cin >> choice;if (choice == 1) {string name;cout << "请输入姓名: ";cin.ignore();getline(cin, name);Contact* contact = book.findByName(name);if (contact) {cout << "\n找到联系人:" << endl;contact->displayDetailed();} else {cout << "未找到姓名为 '" << name << "' 的联系人!" << endl;}} else if (choice == 2) {string phone;cout << "请输入电话: ";cin.ignore();getline(cin, phone);Contact* contact = book.findByPhone(phone);if (contact) {cout << "\n找到联系人:" << endl;contact->displayDetailed();} else {cout << "未找到电话为 '" << phone << "' 的联系人!" << endl;}} else {cout << "无效的选择!" << endl;}
}// 删除联系人菜单
void deleteContactMenu(AddressBook& book) {int choice;cout << "\n=== 删除联系人 ===" << endl;cout << "1. 按姓名删除" << endl;cout << "2. 按电话删除" << endl;cout << "请选择删除方式: ";cin >> choice;if (choice == 1) {string name;cout << "请输入要删除的姓名: ";cin.ignore();getline(cin, name);book.deleteByName(name);} else if (choice == 2) {string phone;cout << "请输入要删除的电话: ";cin.ignore();getline(cin, phone);book.deleteByPhone(phone);} else {cout << "无效的选择!" << endl;}
}// 模糊搜索菜单
void fuzzySearchMenu(AddressBook& book) {string keyword;cout << "\n=== 模糊搜索 ===" << endl;cout << "请输入搜索关键词: ";cin.ignore();getline(cin, keyword);vector<Contact*> results = book.searchByName(keyword);if (results.empty()) {cout << "没有找到包含 '" << keyword << "' 的联系人!" << endl;} else {cout << "\n搜索结果 (共 " << results.size() << " 个):" << endl;cout << string(85, '-') << endl;cout << left << setw(15) << "姓名" << setw(15) << "电话" << setw(25) << "邮箱" << setw(30) << "地址" << endl;cout << string(85, '-') << endl;for (size_t i = 0; i < results.size(); ++i) {results[i]->display();}}
}// 主函数
int main() {AddressBook book;int choice;// 添加一些示例数据book.addContact(Contact("张三", "13800138000", "zhangsan@email.com", "北京市朝阳区"));book.addContact(Contact("李四", "13900139000", "lisi@email.com", "上海市浦东新区"));book.addContact(Contact("王五", "13700137000", "wangwu@email.com", "广州市天河区"));cout << "欢迎使用通讯录管理系统!" << endl;cout << "已预置 " << book.getContactCount() << " 个示例联系人" << endl;while (true) {showMenu();cin >> choice;switch (choice) {case 1:addContactMenu(book);break;case 2:searchContactMenu(book);break;case 3:deleteContactMenu(book);break;case 4:book.displayAll();break;case 5:book.sortByName();break;case 6:book.sortByPhone();break;case 7:fuzzySearchMenu(book);break;case 0:cout << "感谢使用通讯录管理系统,再见!" << endl;return 0;default:cout << "无效的选择,请重新输入!" << endl;break;}cout << "\n按任意键继续...";cin.ignore();cin.get();}return 0;
}
界面预览:
2. 核心功能详解
1️⃣联系人类设计
首先,我们需要一个Contact
类来表示联系人:
class Contact {
private:string name; // 姓名string phone; // 电话号码 string email; // 邮箱string address; // 地址public:// 构造函数Contact(const string& n, const string& p, const string& e = "", const string& a = "") : name(n), phone(p), email(e), address(a) {}// 获取器方法string getName() const { return name; }string getPhone() const { return phone; }// ... 其他方法
};
设计亮点:
- 使用私有成员变量保证数据封装
- 提供公有访问器方法确保接口清晰
- 默认参数让构造更灵活
2️⃣添加联系人
防重复的智慧
bool addContact(const Contact& contact) {// 检查重复:姓名和电话都不能重复if (nameIndex.find(contact.getName()) != nameIndex.end()) {cout << "错误: 姓名已存在!" << endl;return false;}contacts.push_back(contact);updateIndexes(); // 更新索引return true;
}
核心思路:
- 先检查,再添加:避免数据重复
- 双重索引验证:姓名和电话都要唯一
- 原子操作:要么成功,要么失败,不存在中间状态
3️⃣快如闪电的查找功能
Contact* findByName(const string& name) {auto it = nameIndex.find(name);if (it != nameIndex.end()) {return &contacts[it->second]; // 通过索引直接定位}return nullptr;
}
技术解析:
- 利用
map
的O(log n)查找效率 - 索引映射:map存储的是在vector中的位置
- 一次查找定位:不需要遍历整个容器
4️⃣模糊搜索的人性化体验
vector<Contact*> searchByName(const string& keyword) {vector<Contact*> results;string lowerKeyword = toLowerCase(keyword);for (size_t i = 0; i < contacts.size(); ++i) {string lowerName = toLowerCase(contacts[i].getName());if (lowerName.find(lowerKeyword) != string::npos) {results.push_back(&contacts[i]);}}return results;
}
用户体验优化:
- 大小写不敏感:输入"zhang"也能找到"Zhang"
- 子串匹配:输入"李"可以找到"李明"、"李华"等
- 返回指针集合:避免不必要的对象拷贝
四、STL容器的巧妙运用
这一章是技术含金量最高的部分,让我们深入探索STL的精妙之处!
1. vector:动态数组的艺术
为什么不用传统数组?
传统的C风格数组有诸多限制:
- 固定大小,无法动态扩容
- 没有边界检查,容易越界
- 缺乏STL算法支持
而vector
就像是超级进化版的数组:
vector<Contact> contacts; // 自动管理内存
contacts.push_back(newContact); // 自动扩容
contacts.size(); // 获取实际大小
contacts[index]; // 支持下标访问
内存管理的智慧:
- 自动扩容:当容量不足时,vector会申请更大空间(通常是原来的2倍)
- 连续存储:所有元素在内存中连续排列,缓存友好
- RAII机制:析构时自动释放内存,避免内存泄漏
2. map:红黑树的高效查找
map在C++ STL中是一个关联容器,基于自平衡二叉搜索树(特别是红黑树)实现,这保证了:
- 查找效率:O(log n)时间复杂度
- 自动排序:键值始终保持有序
- 稳定性能:无论数据如何插入,性能都很稳定
map<string, int> nameIndex;
// 插入:O(log n)
nameIndex["张三"] = 0;
// 查找:O(log n)
auto it = nameIndex.find("张三");
// 删除:O(log n)
nameIndex.erase("张三");
3. 双容器协作的设计模式
这是我们系统的核心创新:
class AddressBook {
private:vector<Contact> contacts; // 主存储map<string, int> nameIndex; // 姓名索引map<string, int> phoneIndex; // 电话索引void updateIndexes() {nameIndex.clear();phoneIndex.clear();for (size_t i = 0; i < contacts.size(); ++i) {nameIndex[contacts[i].getName()] = i;phoneIndex[contacts[i].getPhone()] = i;}}
};
设计优势:
- vector负责存储:利用连续内存的高效访问
- map负责索引:提供快速查找能力
- 智能同步:修改时自动更新所有索引
4. STL算法的强大助力
STL不仅提供容器,还有丰富的算法库:
// 排序算法
sort(contacts.begin(), contacts.end(), NameComparator());// 查找算法
auto it = find_if(contacts.begin(), contacts.end(), [&](const Contact& c) { return c.getName() == name; });// 转换算法
transform(name.begin(), name.end(), name.begin(), ::tolower);
算法优势:
- 高度优化:经过无数次优化的高效实现
- 泛型设计:适用于所有符合条件的容器
- 标准化:代码更易读、易维护
五、用户体验与界面优化
好的系统不仅要技术过硬,还要有出色的用户体验。让我们看看如何让用户爱上我们的通讯录!
1. 美观的界面设计
void showMenu() {cout << "\n" << string(50, '=') << endl;cout << " 通讯录管理系统" << endl;cout << string(50, '=') << endl;cout << "1. 添加联系人" << endl;cout << "2. 查找联系人" << endl;// ... 更多选项cout << string(50, '-') << endl;cout << "请选择操作 (0-7): ";
}
界面设计理念:
- 视觉层次:用分隔线区分不同区域
- 信息对称:选项排列整齐美观
- 操作引导:清晰的提示和说明
2. 智能的错误处理
bool deleteByName(const string& name) {auto it = nameIndex.find(name);if (it != nameIndex.end()) {contacts.erase(contacts.begin() + it->second);updateIndexes();cout << "联系人删除成功!" << endl;return true;}cout << "未找到该联系人!" << endl; // 友好的错误提示return false;
}
错误处理策略:
- 预防性检查:操作前先验证
- 友好提示:用自然语言告知结果
- 优雅降级:失败时不崩溃,给出建议
3. 数据展示的艺术
void displayAll() const {cout << left << setw(15) << "姓名" << setw(15) << "电话" << setw(25) << "邮箱" << setw(30) << "地址" << endl;cout << string(85, '-') << endl;for (const auto& contact : contacts) {contact.display();}
}
展示优化技巧:
- 表格对齐:使用
setw()
控制列宽 - 分隔美化:用横线分割标题和内容
- 信息层次:重要信息突出显示
六、总结与建议
经过这一路的探索,我们成功构建了一个功能完整、性能优异的通讯录管理系统。让我们回顾一下这个项目的亮点和未来的扩展可能。
1. 项目亮点总结
技术架构方面:
- STL容器双剑合璧:vector + map的完美组合
- 面向对象设计:清晰的类层次和职责分离
- 高效算法应用:O(log n)查找,O(n log n)排序
用户体验方面:
- 操作简便:直观的菜单导航
- 功能完整:增删改查一应俱全
- 错误友好:完善的异常处理机制
代码质量方面:
- 兼容性强:支持Dev-C++ 5.11等多种编译器
- 扩展性好:模块化设计便于功能扩展
- 维护性佳:代码结构清晰,注释完善
2. 性能优化建议
对于追求极致性能的开发者,还可以考虑以下优化:
// 使用unordered_map提升查找速度(O(1)平均时间复杂度)
unordered_map<string, int> fastIndex;// 预留容器空间,减少重新分配
contacts.reserve(1000);// 使用移动语义减少拷贝开销
contacts.push_back(std::move(newContact));
3. 学习收获与启发
通过这个项目,我们不仅掌握了C++和STL的核心概念,更重要的是学会了:
- 系统性思考:如何设计一个完整的软件系统
- 性能意识:时间复杂度和空间复杂度的权衡
- 用户视角:从技术实现转向用户体验
正如《C++ 语言超详细系统学习路线》中提到的,C++最大的特点就是语法复杂,学习难度大,但一旦掌握,就能写出高性能的优秀程序。
在这个AI蓬勃发展的时代,C++依然是系统底层开发的不二选择。正如技术报告所显示的,C++在嵌入式系统、游戏开发、金融科技等关键领域依然发挥着不可替代的作用。
希望这个项目能激发你对编程的热情,让你在C++的世界里越走越远。每一行代码不单是对更好的追求,也是成长的阶梯!
参考资料:
- C++ STL 教程 | 菜鸟教程
- STL教程:C++ STL快速入门 - C语言中文网
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)
如果这篇文章对你有帮助,请不要忘记点赞👍和收藏⭐!有任何问题欢迎在评论区交流讨论~