写程序时,谁都希望它顺顺利利跑完。但现实没那么理想,比如读文件时发现文件被删了,或者内存不够分配了,这些“意外”一旦发生,程序可能直接崩溃。
C++ 提供了一套机制来处理这类问题——异常捕获。它就像给程序加了个“安全气囊”,出问题时能及时响应,而不是一头撞墙。
什么是异常捕获
异常捕获的核心是 try-catch 结构。你把可能出问题的代码放进 try 块里,然后用 catch 去“接住”抛出来的异常。就像你预感到可能会下雨,提前带伞,真下了也不至于淋湿。
try {
int* arr = new int[10000000000]; // 可能因内存不足抛出异常
} catch (const std::bad_alloc& e) {
std::cout << "内存分配失败:" << e.what() << std::endl;
}
上面这段代码尝试分配一大块内存,如果系统撑不住,就会抛出 std::bad_alloc 异常,而 catch 能捕获它,输出提示信息,程序还能继续运行。
捕获不同类型的异常
异常不只一种,C++ 标准库定义了多种异常类型,比如 std::out_of_range 用于访问越界,std::invalid_argument 用于非法参数等。你可以根据需要分别处理。
try {
std::vector<int> vec = {1, 2, 3};
std::cout << vec.at(10) << std::endl; // 越界访问
} catch (const std::out_of_range& e) {
std::cout << "索引越界:" << e.what() << std::endl;
}
这里用了 at() 方法,它会在越界时自动抛出异常,比直接用 [] 安全得多。
别忘了 throw 自己抛异常
除了系统抛的,你也可以自己 throw 异常。比如写一个除法函数,除数为 0 时就主动报错。
double divide(double a, double b) {
if (b == 0) {
throw std::invalid_argument("除数不能为零");
}
return a / b;
}
// 使用时
try {
double result = divide(10, 0);
} catch (const std::exception& e) {
std::cout << "错误:" << e.what() << std::endl;
}
这样调用者就能清楚知道哪里出了问题,而不是得到一个莫名其妙的结果。
注意资源释放
在异常发生时,普通局部变量会自动析构,但如果你手动 new 了内存,又没用智能指针,就容易泄漏。建议优先使用 std::unique_ptr 或 std::vector 这类能自动管理资源的工具。
try {
auto ptr = std::make_unique<int>(42);
// 即使这里抛异常,ptr 也会自动释放
if (some_error) {
throw std::runtime_error("出错了");
}
} catch (...) {
std::cout << "异常被捕获" << std::endl;
}
智能指针配合异常使用,能大大减少出错概率。
异常捕获不是万能药,也不能滥用。频繁抛异常会影响性能,但它确实是提升程序健壮性的重要手段。尤其是在用户输入不可控、外部资源不稳定的情况下,多一层检查,少一分崩溃风险。