关于try-catch异常处理机制

一、他是干什么用的?

try-catch 用于捕获和处理程序中可能发生的错误或异常情况。

当程序运行中出现意外问题时(如除以零、非法访问数组等),可以通过 try-catch 捕获这些异常,避免程序直接崩溃,并提供相应的错误处理逻辑。

二、怎么用的?

1.基本结构

try {

// 编写可能引发异常的代码

}

catch (异常类型& 异常变量) {

// 对异常进行处理

}

catch (...) {

// 捕获未明确处理的异常(兜底)

}

eg:

#include

#include // 用于 std::runtime_error

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(new int[10]); // RAII 确保异常发生时内存被释放

// 如果发生异常,ptr 会自动释放内存

throw std::runtime_error("An error occurred!");

}

Top