首先定义Contact类封装联系人信息,再通过AddressBookManager类用std::vector管理联系人,实现增删改查及文件持久化功能。

C++实现一个简单的通讯录管理系统,核心思路其实就是用类来封装联系人信息,然后用一个容器(比如
std::vector
)来存放这些联系人对象,最后通过一系列函数来对这个容器进行增删改查操作。这听起来可能有点抽象,但实践起来,就是围绕着“数据结构”和“操作逻辑”这两个点展开。
解决方案
要构建一个C++通讯录管理系统,我们通常会从定义联系人的数据结构开始,然后是管理这些联系人的核心类,最后是用户交互界面。
联系人数据结构 (Contact Class)创建一个
Contact
类来表示一个联系人。这个类应该包含联系人的基本信息,比如姓名、电话号码、电子邮件和地址。
#include #include #include #include // 用于文件操作#include // 用于清理输入缓冲区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 contacts; std::string filename = "contacts.txt"; // 数据存储文件名 void clearInputBuffer() { std::cin.ignore(std::numeric_limits::max(), 'n'); }public: AddressBookManager() { loadContacts(); // 构造时尝试加载数据 } ~AddressBookManager() { saveContacts(); // 析构时保存数据 } // 添加联系人 void addContact() { std::string name, phone, email, address; std::cout <> name; std::cout <> 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 <> 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 <> 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 <> 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 <> choice; // 处理输入错误,防止无限循环 if (std::cin.fail()) { std::cout << "无效输入,请重新输入数字。" << std::endl; std::cin.clear(); // 清除错误标志 std::cin.ignore(std::numeric_limits::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;}
这个解决方案涵盖了通讯录的基本功能,并且考虑了数据持久化,使得程序关闭后数据不会丢失。
在C++中,设计一个联系人数据结构时,有哪些关键考量?
设计联系人数据结构,其实就是决定一个
Contact
类里面要放些什么,以及这些东西该用什么类型。从我个人经验来看,这不单单是把字段堆砌起来,更要考虑后续的扩展性、易用性,还有一些实际操作中的小细节。
立即学习“C++免费学习笔记(深入)”;
首先,核心字段肯定少不了:姓名、电话号码是必须的。姓名用
std::string
毫无疑问,电话号码我个人也倾向于用
std::string
。为什么不用
long long
或者其他数字类型呢?原因很简单:电话号码可能包含区号前的
+
号、括号、横线等非数字字符,而且国内的电话号码以0开头,如果用数字类型存储,这个0就会被自动省略,这显然是不对的。此外,电话号码本身很少需要进行数值运算,所以字符串是最合适的选择。
其次,可选字段像电子邮件和地址,也应该用
std::string
。这些信息可能不是每个联系人都具备,所以在构造函数或者添加联系人时,应该允许它们为空。在
Contact
类里,我通常会给它们设置默认值为空字符串,这样在显示时,如果为空就可以选择不显示,让输出更整洁。
再者,封装性。虽然对于一个简单的系统,把所有成员变量设为
public
可能更直接,但从良好的编程习惯和未来扩展的角度看,使用
private
成员变量并通过
public
的getter/setter方法来访问和修改,会更好。这样可以控制数据的有效性,比如在setter里加入数据校验逻辑。不过,对于这个“简单”通讯录,我直接用了
public
,图个方便,毕竟是快速实现嘛。
还有一点,就是唯一标识符。现在这个简单系统是靠姓名来识别联系人的,但现实中,重名的情况并不少见。如果通讯录规模稍大,或者对数据一致性要求高,我就会考虑给每个联系人添加一个唯一的ID(比如
int
或
UUID
)。这样在修改或删除时,就可以通过ID来精确操作,避免了重名带来的混淆。这个ID可以由系统自动生成,比如一个递增的整数。
最后,操作符重载也是一个可以考虑的点。比如重载
<<
操作符,让
std::cout << myContact;
就能直接打印出联系人的完整信息,这会使得代码更加简洁和易读。不过,在上面的示例中,我只是提供了一个
display()
方法,这对于初学者来说可能更直观一些。
总的来说,设计一个数据结构,更多的是在考虑“它要承载什么信息”、“这些信息怎么最恰当地存储”、“未来可能有哪些操作会用到它”,以及“如何让它用起来更顺手”。
如何高效地实现通讯录的增删改查功能?
高效实现通讯录的增删改查(CRUD)功能,对于一个简单的C++通讯录来说,关键在于选择合适的数据结构来存储联系人,以及如何编写这些操作的逻辑。
就我个人经验而言,对于这种规模不大的通讯录(比如几十、几百个联系人),
std::vector
是一个非常好的选择。它使用起来非常直观,而且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++如何实现简单的通讯录管理系统的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1475527.html
微信扫一扫
支付宝扫一扫