一、他是干什么用的?
try-catch 用于捕获和处理程序中可能发生的错误或异常情况。
当程序运行中出现意外问题时(如除以零、非法访问数组等),可以通过 try-catch 捕获这些异常,避免程序直接崩溃,并提供相应的错误处理逻辑。
二、怎么用的?
1.基本结构
try {
// 编写可能引发异常的代码
}
catch (异常类型& 异常变量) {
// 对异常进行处理
}
catch (...) {
// 捕获未明确处理的异常(兜底)
}
eg:
#include
#include
using namespace std;
int main() {
try {
// 模拟抛出异常
int a = 10, b = 0;
if (b == 0) {
throw runtime_error("Division by zero!"); // 抛出异常
}
cout << a / b << endl; // 这行不会被执行
}
catch (const runtime_error& e) { // 捕获并处理 runtime_error 类型的异常
cout << "Error: " << e.what() << endl; // 输出异常信息
}
catch (...) { // 捕获其他所有类型的异常
cout << "An unknown error occurred." << endl;
}
return 0;
}
try里是原来我认为可能出现问题的代码,我把他用try包含住;
(1)已知问题:可以根据已经知道的错误,直接用 throw 抛出异常并通过相应类型的 catch 块捕获它。比如除数为零的情况,可以使用 throw runtime_error("Division by zero!") 抛出异常,并在 catch (const runtime_error& e) 中捕获。
(2)不确定问题:如果不确定错误的类型,或者希望捕获所有可能的错误,可以使用 catch (...) 来捕获所有异常。但这通常是最后的手段,最好在程序中明确捕获具体类型的异常。
(3)e.what() 输出的内容通常是你在抛出异常时传递给构造函数的错误信息。
2.一个简单的小练习例子
练习:处理不同类型的异常
问题描述:
假设你正在写一个简单的计算器程序,用户输入两个数字并选择操作符(加、减、乘、除)。程序需要处理:
1.除数为零的情况。
2.用户输入的非数字字符。
3.用户输入的无效操作符(比如除法符号输入错误)。
#include
#include
#include
using namespace std;
// 自定义异常类:无效操作符
class InvalidOperatorError : public exception {
public:
const char* what() const noexcept override {
return "Invalid operator entered!";
}
};
// 自定义异常类:无效数字输入
class InvalidInputError : public exception {
public:
const char* what() const noexcept override {
return "Invalid number input!";
}
};
int main() {
double num1, num2;
char op;
try {
// 输入第一个数字
cout << "Enter first number: ";
if (!(cin >> num1)) {
throw InvalidInputError(); // 抛出无效数字输入异常
}
// 输入操作符
cout << "Enter operator (+, -, *, /): ";
cin >> op;
// 如果是无效操作符,抛出异常
if (op != '+' && op != '-' && op != '*' && op != '/') {
throw InvalidOperatorError(); // 抛出无效操作符异常
}
// 输入第二个数字
cout << "Enter second number: ";
if (!(cin >> num2)) {
throw InvalidInputError(); // 抛出无效数字输入异常
}
// 如果操作符是除法,检查除数是否为零
if (op == '/' && num2 == 0) {
throw runtime_error("Division by zero!"); // 抛出除零异常
}
// 根据操作符计算结果
double result;
switch (op) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
result = num1 / num2;
break;
default:
throw InvalidOperatorError(); // 额外的安全性检查
}
// 输出结果
cout << "Result: " << result << endl;
}
catch (const InvalidInputError& e) {
cout << "Error: " << e.what() << endl;
}
catch (const InvalidOperatorError& e) {
cout << "Error: " << e.what() << endl;
}
catch (const runtime_error& e) {
cout << "Error: " << e.what() << endl;
}
catch (...) {
cout << "An unknown error occurred." << endl;
}
return 0;
}
解释:
1.异常类:
InvalidOperatorError:用于捕获无效操作符(不是 +, -, * 或 /)的异常。
InvalidInputError:用于捕获无效数字输入(比如用户输入字母或其他无法转换为数字的字符)。
2.try 块:
我们在 try 块中要求用户输入两个数字和一个操作符。所有输入都会被验证。如果有任何问题(如无效输入或除数为零),我们会通过 throw 抛出相应的异常。
3.catch 块:
我们使用多个 catch 块来捕获不同类型的异常,确保能够提供准确的错误信息。
第一个 catch 捕获无效的数字输入,第二个捕获无效操作符,第三个捕获除零错误,最后一个 catch 捕获任何未明确捕获的异常。
三、能解决什么问题?
错误分离:让错误处理从正常逻辑中分离,提高代码可读性。错误传播:支持自动的错误传播,错误在层次结构中逐步传递,减少了代码中的重复错误处理。资源管理:确保资源在异常发生时能够得到正确释放。程序健壮性:能够处理不可预见的错误,提高程序的稳定性。清晰的错误信息:能够提供详细的错误信息,帮助调试和定位问题。灵活的处理策略:为不同类型的异常提供不同的处理策略。简化错误检查:减少显式的错误检查代码,提高开发效率。
四、注意事项?
1. 避免滥用异常控制程序流
异常应该用于处理错误,而不是作为常规控制流程的手段。频繁抛出异常会导致性能下降,并增加代码复杂度。如:(错误例子)
for (int i = 0; i < 1000; ++i) {
try {
if (i == 500) throw std::out_of_range("Reached halfway!");
} catch (...) {
continue;
}
}
不能使用异常来中断循环、跳过某些步骤或返回特定结果。
2. 捕获特定类型的异常
尽量避免使用 catch(...) 捕获所有异常,这样会隐藏错误的根本原因。应根据异常类型捕获并处理,确保问题能够被清晰地定位。错误:
try {
// 某些可能抛出异常的代码
} catch (...) {
std::cout << "An unknown error occurred!" << std::endl;
}
推荐:
try {
// 某些可能抛出异常的代码
} catch (const std::runtime_error& e) {
std::cout << "Runtime error: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cout << "General exception: " << e.what() << std::endl;
}
3. 不要忽略异常
捕获异常后应采取适当的处理措施,如记录日志、清理资源或重新抛出异常,不要简单忽略。否则可能导致程序进入不一致状态。错误:
try {
// 执行某些操作
} catch (const std::exception& e) {
// 什么也不做
}
推荐:
try {
// 执行某些操作
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
// 例如,记录日志、清理资源等
throw; // 重新抛出异常,向上层传递
}
4. 保证异常安全
确保程序在抛出异常时不会导致资源泄漏或程序状态不一致。使用 RAII 等资源管理技巧确保异常发生时自动释放资源。错误:
void foo() {
int* ptr = new int[10]; // 分配内存
// 在异常发生时未释放内存
throw std::runtime_error("An error occurred!");
}
推荐:
void foo() {
std::unique_ptr
// 如果发生异常,ptr 会自动释放内存
throw std::runtime_error("An error occurred!");
}