9.1 异常与捕获
异常(exception)是程序运行过程中出现的非正常情况。异常不一定意味着程序必须崩溃;如果提前设计好处理方式,程序可以给出提示、记录问题,或者继续执行后续逻辑。
常见异常
常见 C++ 标准异常包括:
std::invalid_argument:参数不能被接受,例如用std::stoi转换"abc"。std::out_of_range:值或下标超出允许范围,例如scores.at(10)。std::runtime_error:通用运行时失败。std::bad_alloc:动态内存分配失败。std::ios_base::failure:当 stream 被设置为抛异常时,输入输出操作失败。std::logic_error:逻辑错误相关异常的基类,例如无效参数。
注意
异常不是用来代替普通条件判断的。能用
if 提前判断的简单规则,通常仍然应该先用 if;异常更适合处理“运行时才知道是否会失败”的情况。正在加载概念检查...
try 与 catch
try 中放可能出错的代码,catch 中放错误处理代码。Java 有 finally,Python 也有 finally;C++ 没有 finally 关键字。C++ 通常用 RAII 处理收尾:局部对象在作用域结束时由析构函数清理,无论作用域是正常结束,还是因为异常而离开。
正在加载交互实验...
cpp
#include <iostream>
#include <stdexcept>
#include <string>
int main() {
std::string dividend_text;
std::string divisor_text;
try {
std::cout << "请输入被除数:";
std::getline(std::cin, dividend_text);
int dividend = std::stoi(dividend_text);
std::cout << "请输入除数:";
std::getline(std::cin, divisor_text);
int divisor = std::stoi(divisor_text);
if (divisor == 0) {
throw std::invalid_argument("除数不能为 0。");
}
int quotient = dividend / divisor;
std::cout << dividend << " / " << divisor << " = " << quotient << std::endl;
} catch (const std::invalid_argument& error) {
std::cout << "输入无效:" << error.what() << std::endl;
} catch (const std::out_of_range& error) {
std::cout << "数字超出范围。" << std::endl;
}
std::cout << "本次计算结束。" << std::endl;
return 0;
}一次可能的运行结果:
text
请输入被除数:21
请输入除数:4
21 / 4 = 5
本次计算结束。C++ 也没有 Python 那样的 try-else。没有异常时才需要执行的代码,通常放在 try 中风险语句之后。整体流程仍然很清楚:try 负责尝试,catch 负责失败,局部对象的析构函数负责作用域结束时的清理。
正在加载概念检查...
捕获具体异常
不要一开始就写过宽的 catch (const std::exception& error)。如果什么异常都捕获,程序可能把真正的 bug 也吞掉,导致问题更难发现。
cpp
try {
int score = std::stoi(line);
} catch (const std::invalid_argument& error) {
std::cout << "成绩必须是整数。" << std::endl;
}上面只捕获 std::invalid_argument,因为这段代码真正预期的失败就是“输入不能转成整数”。
正在加载概念检查...
throw
throw 可以主动抛出异常。它适合表达“这个输入不满足函数要求”,让错误在正确的位置被发现,再交给调用者处理。
正在加载交互实验...
cpp
#include <iostream>
#include <stdexcept>
#include <string>
double normalize_score(int score) {
if (score < 0 || score > 100) {
throw std::invalid_argument("成绩必须在 0 到 100 之间。");
}
return score / 100.0;
}
int main() {
std::string text;
try {
std::cout << "请输入成绩:";
std::getline(std::cin, text);
int score = std::stoi(text);
double ratio = normalize_score(score);
std::cout << "成绩比例:" << ratio << std::endl;
} catch (const std::invalid_argument& error) {
std::cout << "输入无效:" << error.what() << std::endl;
}
return 0;
}一次可能的运行结果:
text
请输入成绩:120
输入无效:成绩必须在 0 到 100 之间。这里 normalize_score() 不负责打印提示,它只负责保证自己的输入合法。外层调用者负责决定怎么提示用户。
正在加载概念检查...
正在加载本节练习...