首页 > 后端开发 > C++ > 正文

C++如何实现简单的通讯录管理系统

P粉602998670
发布: 2025-09-16 10:37:01
原创
308人浏览过
首先定义Contact类封装联系人信息,再通过AddressBookManager类用std::vector管理联系人,实现增删改查及文件持久化功能。

c++如何实现简单的通讯录管理系统

C++实现一个简单的通讯录管理系统,核心思路其实就是用类来封装联系人信息,然后用一个容器(比如

std::vector
登录后复制
)来存放这些联系人对象,最后通过一系列函数来对这个容器进行增删改查操作。这听起来可能有点抽象,但实践起来,就是围绕着“数据结构”和“操作逻辑”这两个点展开。

解决方案

要构建一个C++通讯录管理系统,我们通常会从定义联系人的数据结构开始,然后是管理这些联系人的核心类,最后是用户交互界面。

  1. 联系人数据结构 (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;
        }
    };
    登录后复制
  2. 通讯录管理类 (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; // 运行时不频繁提示
        }
    };
    登录后复制
  3. 主程序 (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;
    }
    登录后复制

    这个解决方案涵盖了通讯录的基本功能,并且考虑了数据持久化,使得程序关闭后数据不会丢失。

在C++中,设计一个联系人数据结构时,有哪些关键考量?

设计联系人数据结构,其实就是决定一个

Contact
登录后复制
类里面要放些什么,以及这些东西该用什么类型。从我个人经验来看,这不单单是把字段堆砌起来,更要考虑后续的扩展性、易用性,还有一些实际操作中的小细节。

立即学习C++免费学习笔记(深入)”;

首先,核心字段肯定少不了:姓名、电话号码是必须的。姓名用

std::string
登录后复制
毫无疑问,电话号码我个人也倾向于用
std::string
登录后复制
为什么不用
long long
登录后复制
或者其他数字类型呢?原因很简单:电话号码可能包含区号前的
+
登录后复制
号、括号、横线等非数字字符,而且国内的电话号码以0开头,如果用数字类型存储,这个0就会被自动省略,这显然是不对的。此外,电话号码本身很少需要进行数值运算,所以字符串是最合适的选择。

其次,可选字段像电子邮件和地址,也应该用

std::string
登录后复制
。这些信息可能不是每个联系人都具备,所以在构造函数或者添加联系人时,应该允许它们为空。在
Contact
登录后复制
类里,我通常会给它们设置默认值为空字符串,这样在显示时,如果为空就可以选择不显示,让输出更整洁。

再者,封装性。虽然对于一个简单的系统,把所有成员变量设为

public
登录后复制
可能更直接,但从良好的编程习惯和未来扩展的角度看,使用
private
登录后复制
成员变量并通过
public
登录后复制
的getter/setter方法来访问和修改,会更好。这样可以控制数据的有效性,比如在setter里加入数据校验逻辑。不过,对于这个“简单”通讯录,我直接用了
public
登录后复制
,图个方便,毕竟是快速实现嘛。

乾坤圈新媒体矩阵管家
乾坤圈新媒体矩阵管家

新媒体账号、门店矩阵智能管理系统

乾坤圈新媒体矩阵管家 17
查看详情 乾坤圈新媒体矩阵管家

还有一点,就是唯一标识符。现在这个简单系统是靠姓名来识别联系人的,但现实中,重名的情况并不少见。如果通讯录规模稍大,或者对数据一致性要求高,我就会考虑给每个联系人添加一个唯一的ID(比如

int
登录后复制
UUID
登录后复制
)。这样在修改或删除时,就可以通过ID来精确操作,避免了重名带来的混淆。这个ID可以由系统自动生成,比如一个递增的整数。

最后,操作符重载也是一个可以考虑的点。比如重载

<<
登录后复制
操作符,让
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++如何实现简单的通讯录管理系统的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号