C++ 使用 try-catch 语句来捕获和处理异常。try 块包含可能发生错误的代码,而 catch 块则用来捕获并处理错误。
try-catch 语句的基本结构
try {// 可能抛出异常的代码
} catch (exception_type1 e1) {// 处理异常类型 1
} catch (exception_type2 e2) {// 处理异常类型 2
} catch (...) {// 捕获所有类型的异常
}
具体解释:
-
try 块
try 块包含可能发生异常的代码。
如果 try 块中的代码发生了异常,程序会跳转到相应的 catch 块进行异常处理。
-
catch 块
catch 用来捕获由 try 块抛出的异常,并进行处理。
可以有多个 catch 块,每个 catch 块负责处理特定类型的异常。
如果 catch 中的异常类型与抛出的异常类型匹配,程序会执行该 catch 块中的代码。
-
catch (...)
catch(...) 是一个通配符,用来捕获所有类型的异常。
这在无法确定抛出异常的具体类型时特别有用
举两个例子说明
-
第一个例子
#include <iostream> using namespace std;int main() {try {int a = 5;int b = 0;if (b == 0) {throw "Division by zero error!";}cout << a / b << endl;} catch (const char* e) {cout << "Error: " << e << endl; // 捕获并处理除零错误}return 0; }
输出如下:
解释:
在 try 块中,程序尝试执行可能出错的代码。
如果发生异常(这里是通过 throw 手动抛出除零错误),catch 块会捕获到异常并进行处理。
-
第二个例子
#include <iostream> #include <stdexcept> // 包含异常类using namespace std;int main() {try {int a = 10, b = 0;if (b == 0) {throw std::runtime_error("Division by zero error!"); // 抛出运行时错误}cout << a / b << endl;} catch (const std::runtime_error& e) {cout << "Caught a runtime error: " << e.what() << endl; // 捕获并处理 std::runtime_error} catch (const std::exception& e) {cout << "Caught a general exception: " << e.what() << endl; // 捕获所有 std::exception 类型的异常} catch (...) {cout << "Caught an unknown error!" << endl; // 捕获其他所有类型的异常}return 0; }
输出如下:
解释:
try 块中的代码试图执行可能会抛出异常的操作。例如,在这个例子中,我们故意将 b 设置为 0,模拟除零错误。
如果程序遇到除零错误,抛出一个 std::runtime_error 类型的异常。
catch (const std::runtime_error& e) 块捕获 std::runtime_error 类型的异常,并输出错误信息。
catch (const std::exception& e) 是更广泛的异常类型,它会捕获所有继承自 std::exception 类的异常(包括 std::runtime_error)。
catch (...) 捕获所有其他类型的异常,确保即使没有明确处理某种异常类型,程序也不会崩溃。
throw 语句:抛出异常
在 C++ 中,throw 关键字用于抛出异常。可以在任何地方抛出异常,通常是在遇到错误或不符合预期的条件时。
throw some_exception; // 抛出一个异常
常见的异常类型
C++ 标准库定义了几种常见的异常类型。可以选择抛出这些异常类型之一,或者创建自己的异常类。
-
std::exception:这是所有标准异常类的基类,所有其他标准异常类都继承自它。
-
std::runtime_error:运行时错误,表示程序运行时发生的错误。
-
std::logic_error:逻辑错误,表示程序的逻辑上有问题,比如越界访问数组等。
-
std::out_of_range:越界错误,通常用于容器类(如 std::vector)访问越界元素时抛出的异常。
-
std::invalid_argument:无效参数错误,表示传递给函数的参数不合法。
-
std::bad_alloc:内存分配错误,当 new 操作失败时抛出此异常。
自定义异常类
也可以创建自己的异常类,继承自 std::exception 或其派生类,以便在程序中抛出和捕获特定类型的异常。
#include <iostream>
#include <exception>class MyException : public std::exception {
public:const char* what() const noexcept override {return "My custom exception occurred!";}
};int main() {try {throw MyException(); // 抛出自定义异常} catch (const MyException& e) {std::cout << "Caught custom exception: " << e.what() << std::endl;}return 0;
}
输出如下:
异常的传播和处理
当 throw 抛出异常时,异常会沿着调用栈向上传递,直到找到一个匹配的 catch 块。
如果没有合适的 catch 块捕获异常,程序会终止并显示未处理的异常信息。
比如:
#include <iostream>
#include <stdexcept> // 包含标准异常类型using namespace std;int main() {try {cout << "Throwing an exception..." << endl;throw std::runtime_error("This is a runtime error!"); // 抛出一个运行时错误}// 这里只捕获 std::logic_error 类型的异常,忽略 std::runtime_errorcatch (const std::logic_error& e) {cout << "Caught logic_error: " << e.what() << endl;}cout << "This will not be printed if an exception is thrown!" << endl;return 0;
}
输出如下:
异常处理的注意事项
异常的安全性:捕获异常时要确保程序的状态一致性。如果在 catch 块中处理异常时发生了额外的错误,可能导致程序不稳定。
性能问题:异常处理会增加程序的运行时开销,尤其是在频繁抛出和捕获异常的情况下。因此,异常处理应该只用于真正的错误,而不是正常的程序流控制。