6、C++项目实战:简易ATM机模拟系统
ATM机,大家都用过吧?插卡、输密码、取钱、查余额,一气呵成。但你想过没有,这些看似简单的操作,背后其实是一套严谨的状态机逻辑。今天我们就来手写一个简易的ATM模拟系统,把那些「屏幕上的按钮」变成实实在在的C++代码。
我个人习惯,做这种模拟项目时,先画一张流程图。别急着写代码,把逻辑理清了,后面才不会乱。来,先看看这个系统的核心流程:
嗯,这张图基本把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里不能直接定义变量,除非用花括号包起来。这个坑我踩过,编译报错时一脸懵,后来才反应过来。
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把控制台换成图形界面。
我个人建议,学完这个项目后,可以试着给它加上「转账」功能。转账涉及到两个账户的余额变更,还要考虑并发问题——虽然单机版不需要锁,但思路可以先培养起来。
好了,今天的项目就到这里。代码不多,但逻辑完整。你可以在本地跑一跑,试着改改密码、调调余额上限,看看边界情况会不会崩溃。嗯,动手试试吧。