
C++实现一个简单的通讯录管理系统,核心思路其实就是用类来封装联系人信息,然后用一个容器(比如
std::vector)来存放这些联系人对象,最后通过一系列函数来对这个容器进行增删改查操作。这听起来可能有点抽象,但实践起来,就是围绕着“数据结构”和“操作逻辑”这两个点展开。 解决方案
要构建一个C++通讯录管理系统,我们通常会从定义联系人的数据结构开始,然后是管理这些联系人的核心类,最后是用户交互界面。
-
联系人数据结构 (Contact Class) 创建一个
Contact
类来表示一个联系人。这个类应该包含联系人的基本信息,比如姓名、电话号码、电子邮件和地址。#include <string> #include <iostream> #include <vector> #include <fstream> // 用于文件操作 #include <limits> // 用于清理输入缓冲区 class Contact { public: std::string name; std::string phoneNumber; std::string email; std::string address; // 默认构造函数 Contact() = default; // 带参数的构造函数 Contact(const std::string& name, const std::string& phone, const std::string& email = "", const std::string& address = "") : name(name), phoneNumber(phone), email(email), address(address) {} // 显示联系人信息 void display() const { std::cout << "姓名: " << name << std::endl; std::cout << "电话: " << phoneNumber << std::endl; if (!email.empty()) std::cout << "邮箱: " << email << std::endl; if (!address.empty()) std::cout << "地址: " << address << std::endl; std::cout << "--------------------" << std::endl; } // 方便保存到文件 std::string toStringForFile() const { return name + "|" + phoneNumber + "|" + email + "|" + address; } // 从文件字符串解析 static Contact fromStringForFile(const std::string& line) { Contact c; size_t pos = 0; size_t next_pos; next_pos = line.find('|', pos); c.name = line.substr(pos, next_pos - pos); pos = next_pos + 1; next_pos = line.find('|', pos); c.phoneNumber = line.substr(pos, next_pos - pos); pos = next_pos + 1; next_pos = line.find('|', pos); c.email = line.substr(pos, next_pos - pos); pos = next_pos + 1; c.address = line.substr(pos); return c; } }; -
通讯录管理类 (AddressBookManager Class) 这个类将负责存储
Contact
对象,并提供增、删、改、查以及数据持久化的功能。class AddressBookManager { private: std::vector<Contact> contacts; std::string filename = "contacts.txt"; // 数据存储文件名 void clearInputBuffer() { std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); } public: AddressBookManager() { loadContacts(); // 构造时尝试加载数据 } ~AddressBookManager() { saveContacts(); // 析构时保存数据 } // 添加联系人 void addContact() { std::string name, phone, email, address; std::cout << "请输入姓名: "; std::cin >> name; std::cout << "请输入电话号码: "; std::cin >> phone; clearInputBuffer(); // 清理缓冲区,防止getline读取到换行符 std::cout << "请输入邮箱 (可选): "; std::getline(std::cin, email); std::cout << "请输入地址 (可选): "; std::getline(std::cin, address); contacts.emplace_back(name, phone, email, address); std::cout << "联系人添加成功!" << std::endl; } // 显示所有联系人 void displayAllContacts() const { if (contacts.empty()) { std::cout << "通讯录为空。" << std::endl; return; } std::cout << "\n----- 所有联系人 -----" << std::endl; for (const auto& contact : contacts) { contact.display(); } std::cout << "--------------------" << std::endl; } // 查找联系人 void searchContact() const { if (contacts.empty()) { std::cout << "通讯录为空,无法查找。" << std::endl; return; } std::string keyword; std::cout << "请输入姓名或电话号码进行查找: "; std::cin >> keyword; clearInputBuffer(); bool found = false; std::cout << "\n----- 查找结果 -----" << std::endl; for (const auto& contact : contacts) { if (contact.name.find(keyword) != std::string::npos || contact.phoneNumber.find(keyword) != std::string::npos) { contact.display(); found = true; } } if (!found) { std::cout << "未找到匹配的联系人。" << std::endl; } std::cout << "--------------------" << std::endl; } // 修改联系人 void modifyContact() { if (contacts.empty()) { std::cout << "通讯录为空,无法修改。" << std::endl; return; } std::string nameToModify; std::cout << "请输入要修改的联系人姓名: "; std::cin >> nameToModify; clearInputBuffer(); bool found = false; for (auto& contact : contacts) { if (contact.name == nameToModify) { std::cout << "找到联系人: " << contact.name << std::endl; std::cout << "请输入新的电话号码 (当前: " << contact.phoneNumber << "): "; std::getline(std::cin, contact.phoneNumber); std::cout << "请输入新的邮箱 (当前: " << contact.email << "): "; std::getline(std::cin, contact.email); std::cout << "请输入新的地址 (当前: " << contact.address << "): "; std::getline(std::cin, contact.address); std::cout << "联系人修改成功!" << std::endl; found = true; break; } } if (!found) { std::cout << "未找到姓名为 '" << nameToModify << "' 的联系人。" << std::endl; } } // 删除联系人 void deleteContact() { if (contacts.empty()) { std::cout << "通讯录为空,无法删除。" << std::endl; return; } std::string nameToDelete; std::cout << "请输入要删除的联系人姓名: "; std::cin >> nameToDelete; clearInputBuffer(); auto it = contacts.begin(); bool found = false; while (it != contacts.end()) { if (it->name == nameToDelete) { it = contacts.erase(it); // 删除并获取下一个迭代器 std::cout << "联系人删除成功!" << std::endl; found = true; break; } else { ++it; } } if (!found) { std::cout << "未找到姓名为 '" << nameToDelete << "' 的联系人。" << std::endl; } } // 保存联系人到文件 void saveContacts() const { std::ofstream outFile(filename); if (!outFile.is_open()) { std::cerr << "错误:无法打开文件 " << filename << " 进行写入。" << std::endl; return; } for (const auto& contact : contacts) { outFile << contact.toStringForFile() << std::endl; } outFile.close(); // std::cout << "通讯录已保存到文件。" << std::endl; // 运行时不频繁提示 } // 从文件加载联系人 void loadContacts() { std::ifstream inFile(filename); if (!inFile.is_open()) { // std::cerr << "提示:文件 " << filename << " 不存在或无法打开,将创建新通讯录。" << std::endl; return; // 文件不存在是正常情况,初次运行会创建 } std::string line; while (std::getline(inFile, line)) { if (!line.empty()) { contacts.push_back(Contact::fromStringForFile(line)); } } inFile.close(); // std::cout << "通讯录已从文件加载。" << std::endl; // 运行时不频繁提示 } }; -
主程序 (main function) 在
main
函数中,创建一个AddressBookManager
对象,并实现一个菜单驱动的循环,让用户选择不同的操作。void showMenu() { 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 << "0. 退出" << std::endl; std::cout << "-------------------------" << std::endl; std::cout << "请选择操作: "; } int main() { AddressBookManager manager; int choice; do { showMenu(); std::cin >> choice; // 处理输入错误,防止无限循环 if (std::cin.fail()) { std::cout << "无效输入,请重新输入数字。" << std::endl; std::cin.clear(); // 清除错误标志 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // 忽略剩余输入 continue; } switch (choice) { case 1: manager.addContact(); break; case 2: manager.displayAllContacts(); break; case 3: manager.searchContact(); break; case 4: manager.modifyContact(); break; case 5: manager.deleteContact(); break; case 0: std::cout << "感谢使用,再见!" << std::endl; break; default: std::cout << "无效选择,请重新输入。" << std::endl; break; } } while (choice != 0); return 0; }这个解决方案涵盖了通讯录的基本功能,并且考虑了数据持久化,使得程序关闭后数据不会丢失。
设计联系人数据结构,其实就是决定一个
Contact类里面要放些什么,以及这些东西该用什么类型。从我个人经验来看,这不单单是把字段堆砌起来,更要考虑后续的扩展性、易用性,还有一些实际操作中的小细节。
首先,核心字段肯定少不了:姓名、电话号码是必须的。姓名用
std::string毫无疑问,电话号码我个人也倾向于用
std::string。为什么不用
long long或者其他数字类型呢?原因很简单:电话号码可能包含区号前的
+号、括号、横线等非数字字符,而且国内的电话号码以0开头,如果用数字类型存储,这个0就会被自动省略,这显然是不对的。此外,电话号码本身很少需要进行数值运算,所以字符串是最合适的选择。
其次,可选字段像电子邮件和地址,也应该用
std::string。这些信息可能不是每个联系人都具备,所以在构造函数或者添加联系人时,应该允许它们为空。在
Contact类里,我通常会给它们设置默认值为空字符串,这样在显示时,如果为空就可以选择不显示,让输出更整洁。
再者,封装性。虽然对于一个简单的系统,把所有成员变量设为
public可能更直接,但从良好的编程习惯和未来扩展的角度看,使用
private成员变量并通过
public的getter/setter方法来访问和修改,会更好。这样可以控制数据的有效性,比如在setter里加入数据校验逻辑。不过,对于这个“简单”通讯录,我直接用了
public,图个方便,毕竟是快速实现嘛。
还有一点,就是唯一标识符。现在这个简单系统是靠姓名来识别联系人的,但现实中,重名的情况并不少见。如果通讯录规模稍大,或者对数据一致性要求高,我就会考虑给每个联系人添加一个唯一的ID(比如
int或
UUID)。这样在修改或删除时,就可以通过ID来精确操作,避免了重名带来的混淆。这个ID可以由系统自动生成,比如一个递增的整数。
Post AI
博客文章AI生成器
50
查看详情
最后,操作符重载也是一个可以考虑的点。比如重载
<<操作符,让
std::cout << myContact;就能直接打印出联系人的完整信息,这会使得代码更加简洁和易读。不过,在上面的示例中,我只是提供了一个
display()方法,这对于初学者来说可能更直观一些。
总的来说,设计一个数据结构,更多的是在考虑“它要承载什么信息”、“这些信息怎么最恰当地存储”、“未来可能有哪些操作会用到它”,以及“如何让它用起来更顺手”。
如何高效地实现通讯录的增删改查功能?高效实现通讯录的增删改查(CRUD)功能,对于一个简单的C++通讯录来说,关键在于选择合适的数据结构来存储联系人,以及如何编写这些操作的逻辑。
就我个人经验而言,对于这种规模不大的通讯录(比如几十、几百个联系人),
std::vector<Contact>是一个非常好的选择。它使用起来非常直观,而且C++标准库已经为它优化了内存管理和许多基本操作。
增加 (Add) 联系人: 这个操作是
std::vector
的强项。当用户输入完联系人信息后,我们创建一个Contact
对象,然后直接调用contacts.push_back(newContact);
。push_back
会在vector
的末尾添加元素,通常效率很高(平均O(1),最坏O(N)当需要重新分配内存时)。这没什么可纠结的,直接用就好。显示 (Display) 所有联系人: 这也很直接,就是遍历
std::vector
。你可以用基于范围的for循环for (const auto& contact : contacts)
,或者传统的迭代器循环。遍历并调用每个Contact
对象的display()
方法即可。这个操作的复杂度是O(N),N是联系人数量,这是不可避免的,因为你得显示所有信息。-
查找 (Search) 联系人: 这是CRUD操作中效率考量比较多的地方。对于
std::vector
,最直接的方式就是线性查找。你可以遍历vector
,检查每个联系人的姓名或电话号码是否包含用户输入的关键词。比如:for (const auto& contact : contacts) { if (contact.name.find(keyword) != std::string::npos || contact.phoneNumber.find(keyword) != std::string::npos) { // 找到并显示 } }std::string::find
会返回std::string::npos
如果没找到子串。这种方式简单易懂,对于小规模数据,其O(N)的复杂度完全可以接受。如果联系人数量巨大,线性查找就会变得很慢,那时我们会考虑std::map
或std::unordered_map
,它们能提供O(logN)或平均O(1)的查找速度,但那会增加系统的复杂性,需要选择一个唯一的键(比如联系人姓名或ID)。对于“简单”通讯录,没必要过度优化。 -
修改 (Modify) 联系人: 修改操作通常是先查找,再修改。用户输入要修改的联系人姓名(或ID),然后我们遍历
vector
找到对应的Contact
对象。一旦找到,直接修改这个对象的成员变量即可。for (auto& contact : contacts) { // 注意这里是引用,以便修改 if (contact.name == nameToModify) { // 获取新数据并更新 contact.phoneNumber, contact.email 等 break; // 找到并修改后就可以退出循环了 } }同样,查找部分是O(N),修改本身是O(1)。
-
删除 (Delete) 联系人: 删除操作稍微有点讲究。找到要删除的联系人后,我们不能直接从
vector
中“抠掉”它。std::vector
提供了erase()
方法。auto it = contacts.begin(); while (it != contacts.end()) { if (it->name ==
以上就是C++如何实现简单的通讯录管理系统的详细内容,更多请关注知识资源分享宝库其它相关文章!
相关标签: word ai c++ ios switch 邮箱 字符串解析 封装性 标准库 为什么 red String for 封装 成员变量 构造函数 标识符 const auto 字符串 int 循环 数据结构 堆 class public private 数字类型 map delete function 对象 display 大家都在看: C++井字棋AI实现 简单决策算法编写 如何为C++搭建边缘AI训练环境 TensorFlow分布式训练配置 怎样用C++开发井字棋AI 简单决策算法实现方案 怎样为C++配置嵌入式AI开发环境 TensorFlow Lite Micro移植指南 C++井字棋游戏怎么开发 二维数组与简单AI逻辑实现






发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。