6、C++项目实战:简易ATM机模拟系统

ATM机,大家都用过吧?插卡、输密码、取钱、查余额,一气呵成。但你想过没有,这些看似简单的操作,背后其实是一套严谨的状态机逻辑。今天我们就来手写一个简易的ATM模拟系统,把那些「屏幕上的按钮」变成实实在在的C++代码。

我个人习惯,做这种模拟项目时,先画一张流程图。别急着写代码,把逻辑理清了,后面才不会乱。来,先看看这个系统的核心流程:

简易ATM机模拟系统 - 核心流程图 系统启动 插卡 / 输入卡号 输入密码 验证 失败 提示错误 成功 主菜单(查询/取款/存款/退出)

嗯,这张图基本把ATM的核心逻辑串起来了。接下来我们一步步实现它。

1. 设计数据结构

先想想,ATM需要存什么?卡号、密码、余额,这三样是铁打的。我建议用一个结构体来封装:

struct Account {
    string cardNumber;   // 卡号
    string password;     // 密码
    double balance;      // 余额
};

你可能会问:「密码存明文?不安全吧?」你说得对。但这是教学项目,咱们先聚焦逻辑。实际项目中,密码一定要哈希处理。我曾经在一个内部工具里偷懒没做哈希,结果被安全审计怼了一顿……从那以后,凡是涉及密码的,我必加一层哈希。

3. 核心功能实现

ATM的核心功能就四个:查询余额、取款、存款、退出。说白了,就是对着一个账户对象做CRUD操作。我们来看看取款这个函数怎么写:

bool withdraw(Account &acc, double amount) {
    if (amount <= 0) {
        cout << "金额必须大于0!" << endl;
        return false;
    }
    if (amount > acc.balance) {
        cout << "余额不足!当前余额:" << acc.balance << endl;
        return false;
    }
    acc.balance -= amount;
    cout << "取款成功!请取走现金:" << amount << " 元" << endl;
    return true;
}

这里有两个边界条件:金额为负和余额不足。你想想看,如果用户输入-100,系统直接扣款,那不就乱套了?所以前置校验一定要做全。

小技巧: 我习惯在函数开头就把所有「快速失败」的条件写完。这样代码逻辑清晰,后面的人读起来也舒服。

3. 密码验证与重试机制

真正的ATM机,密码输错三次会吞卡。咱们也模拟这个机制:

bool verifyPassword(Account &acc, int maxRetries = 3) {
    for (int i = 0; i < maxRetries; ++i) {
        string input;
        cout << "请输入密码(剩余" << maxRetries - i << "次机会): ";
        cin >> input;
        if (input == acc.password) {
            cout << "验证通过!" << endl;
            return true;
        }
        cout << "密码错误!" << endl;
    }
    cout << "密码连续错误" << maxRetries << "次,账户已锁定!" << endl;
    return false;
}

这里用了一个for循环来控制重试次数。我曾经见过有人用while(true)+break来实现,结果忘记加跳出条件,变成了死循环——用户永远在输密码,永远出不去。嗯,这种低级错误,咱们就别犯了。

4. 主菜单与状态流转

ATM的核心是一个状态机。每次操作后,要么继续,要么退出。我习惯用do-while循环来写这种「至少执行一次」的菜单:

void showMenu(Account &acc) {
    int choice;
    do {
        cout << "\n========== 主菜单 ==========" << endl;
        cout << "1. 查询余额" << endl;
        cout << "2. 取款" << endl;
        cout << "3. 存款" << endl;
        cout << "4. 退出" << endl;
        cout << "请选择操作: ";
        cin >> choice;

        switch (choice) {
            case 1:
                cout << "当前余额: " << acc.balance << " 元" << endl;
                break;
            case 2: {
                double amount;
                cout << "请输入取款金额: ";
                cin >> amount;
                withdraw(acc, amount);
                break;
            }
            case 3: {
                double amount;
                cout << "请输入存款金额: ";
                cin >> amount;
                if (amount > 0) {
                    acc.balance += amount;
                    cout << "存款成功!当前余额: " << acc.balance << " 元" << endl;
                } else {
                    cout << "存款金额必须大于0!" << endl;
                }
                break;
            }
            case 4:
                cout << "感谢使用,再见!" << endl;
                break;
            default:
                cout << "无效选项,请重新选择!" << endl;
        }
    } while (choice != 4);
}

注意switch里的case 2和case 3,我加了花括号。为什么?因为要在case里定义局部变量。C++的语法规定,case里不能直接定义变量,除非用花括号包起来。这个坑我踩过,编译报错时一脸懵,后来才反应过来。

注意: switch的每个case末尾一定要加break,否则会「穿透」到下一个case。如果你忘了加,用户选「查询余额」可能会直接跳到「取款」界面去。这可不是我们想要的效果。

5. 完整流程串联

好了,现在把上面这些零件拼起来。main函数里只需要做三件事:初始化账户、验证密码、进入菜单:

int main() {
    // 初始化一个测试账户
    Account acc = {"10086", "123456", 5000.0};

    cout << "欢迎使用简易ATM系统" << endl;
    cout << "请插入您的银行卡(输入卡号): ";
    string inputCard;
    cin >> inputCard;

    if (inputCard != acc.cardNumber) {
        cout << "未识别的卡号,系统退出。" << endl;
        return 1;
    }

    if (!verifyPassword(acc)) {
        return 1;  // 密码验证失败,直接退出
    }

    showMenu(acc);
    return 0;
}

你看,整个流程非常清晰。先校验卡号,再校验密码,最后进入菜单。每一步都有明确的退出条件,不会出现「卡号错了还能继续操作」的诡异情况。

6. 扩展思考

这个项目虽然简单,但它的架构可以扩展到很多真实场景。比如:

  • 多账户支持:用vector<Account>存一批账户,卡号作为索引。
  • 交易记录:每次操作都写一条日志到文件,方便审计。
  • GUI界面:用Qt或Win32把控制台换成图形界面。

我个人建议,学完这个项目后,可以试着给它加上「转账」功能。转账涉及到两个账户的余额变更,还要考虑并发问题——虽然单机版不需要锁,但思路可以先培养起来。

核心收获: 这个项目教会你的不是ATM本身,而是「状态机思维」。任何需要「插卡→验证→操作→退出」这种流程的系统,都可以用同样的模式来设计。你想想看,自动售货机、门禁系统、甚至游戏里的NPC对话,是不是都长这样?

好了,今天的项目就到这里。代码不多,但逻辑完整。你可以在本地跑一跑,试着改改密码、调调余额上限,看看边界情况会不会崩溃。嗯,动手试试吧。


公众号:蓝海资料掘金营,微信deep3321