异常是在程序执行期间发生的意外事件。例如,
double divide_by_zero = 7 / 0;
以上代码会引发一个异常,因为一个数字不能被 0 整除。
在 C++ 中,处理这些类型错误的过程称为异常处理。
在 C++ 中,我们借助 try 和 catch 块以及 throw 关键字来处理异常。
try- 可能引发异常的代码throw- 检测到错误时引发异常catch- 处理throw关键字引发的异常的代码
注意: throw 语句不是必需的,特别是如果我们使用标准的 C++ 异常。
C++ 异常处理的语法
C++ 异常处理的基本语法如下所示
try {
// code that may raise an exception
throw argument;
}
catch (exception) {
// code to handle exception
}
在这里,我们将可能生成异常的代码放在 try 块中。每个 try 块后面都有一个 catch 块。
当发生异常时,throw 语句会引发一个异常,该异常会被 catch 块捕获。
catch 块不能在没有 try 块的情况下使用。
示例 1:C++ 异常处理
// program to divide two numbers
// throws an exception when the divisor is 0
#include <iostream>
using namespace std;
int main() {
double numerator, denominator, divide;
cout << "Enter numerator: ";
cin >> numerator;
cout << "Enter denominator: ";
cin >> denominator;
try {
// throw an exception if denominator is 0
if (denominator == 0)
throw 0;
// not executed if denominator is 0
divide = numerator / denominator;
cout << numerator << " / " << denominator << " = " << divide << endl;
}
catch (int num_exception) {
cout << "Error: Cannot divide by " << num_exception << endl;
}
return 0;
}
输出 1
Enter numerator: 72 Enter denominator: 0 Error: Cannot divide by 0
输出 2
Enter numerator: 72 Enter denominator: 3 72 / 3 = 24
上面的程序将两个数字相除并显示结果。但是,如果分母为 0,则会发生异常。
为了处理异常,我们将代码 divide = numerator / denominator; 放在 try 块中。现在,当发生异常时,try 块内的其余代码将被跳过。
catch 块捕获抛出的异常并执行其中的语句。
如果 try 块中的任何语句都没有生成异常,则会跳过 catch 块。
请注意,我们使用代码 throw 0; 抛出了 int 字面量 0。
我们可以根据情况和我们想在 catch 块中执行的内容来抛出任何字面量、变量或类。
catch 参数 int num_exception 接收由 throw 语句传递的值,即字面量 0。
捕获所有类型的异常
在异常处理中,了解我们的 try 语句中的代码可能发生的异常类型非常重要。
这样我们就可以使用适当的 catch 参数。否则,try...catch 语句可能无法正常工作。
如果我们不知道 try 块中可能发生的异常类型,那么我们可以使用省略号 ... 作为我们的 catch 参数。
try {
// code
}
catch (...) {
// code
}
C++ 多个 catch 语句
在 C++ 中,我们可以为来自单个代码块的各种异常使用多个 catch 语句。
try {
// code
}
catch (exception1) {
// code
}
catch (exception2) {
// code
}
catch (...) {
// code
}
在这里,如果发生 exception1,我们的程序就会捕获它。如果没有,如果发生 exception2,它就会捕获它。
如果发生既不是 exception1 也不是 exception2 的错误,那么将执行 catch (...) {} 中的代码。
注意事项
catch (...) {}块应始终放在我们try...catch语句的末尾。这是因为此块捕获所有可能的异常并充当默认的catch块。
- 在我们的代码中包含默认的
catch块不是强制性的。
示例 2:C++ 多个 catch 语句
此程序将两个数字相除并将结果存储在数组元素中。此程序可能发生两种异常
- 如果数组越界,即如果数组的索引大于数组的大小
- 如果一个数除以 0
这些异常在多个 catch 语句中被捕获。
#include <iostream>
using namespace std;
int main() {
double numerator, denominator, arr[4] = {0.0, 0.0, 0.0, 0.0};
int index;
cout << "Enter array index: ";
cin >> index;
try {
// throw exception if array out of bounds
if (index >= 4)
throw "Error: Array out of bounds!";
// not executed if array is out of bounds
cout << "Enter numerator: ";
cin >> numerator;
cout << "Enter denominator: ";
cin >> denominator;
// throw exception if denominator is 0
if (denominator == 0)
throw 0;
// not executed if denominator is 0
arr[index] = numerator / denominator;
cout << arr[index] << endl;
}
// catch "Array out of bounds" exception
catch (const char* msg) {
cout << msg << endl;
}
// catch "Divide by 0" exception
catch (int num) {
cout << "Error: Cannot divide by " << num << endl;
}
// catch any other exception
catch (...) {
cout << "Unexpected exception!" << endl;
}
return 0;
}
输出 1
Enter array index: 5 Error: Array out of bounds!
这里,数组 arr 只有 4 个元素。因此,index 不能大于 3。
在这种情况下,index 为 5。所以我们抛出一个字符串字面量 "Error: Array out of bounds!"。此异常被第一个 catch 块捕获。
请注意 catch 参数 const char* msg。这表明 catch 语句将字符串字面量作为参数。
输出 2
Enter array index: 2 Enter numerator: 5 Enter denominator: 0 Error: Cannot divide by 0
这里,分母是 0。所以我们抛出 int 字面量 0。此异常被第二个 catch 块捕获。
如果发生任何其他异常,它将被默认的 catch 块捕获。
输出 3
Enter array index: 2 Enter numerator: 5 Enter denominator: 2 2.5
这里,程序运行没有任何问题,因为没有发生异常。
C++ 标准异常
C++ 为我们提供了许多标准异常,我们可以在异常处理中使用它们。其中一些显示在下表中。
| 异常 | 描述 |
|---|---|
std::exception |
所有 C++ 异常的父类。 |
std::bad_alloc |
当动态内存分配失败时抛出。 |
std::bad_cast |
当尝试对无效类型执行 dynamic_cast 时由 C++ 抛出。 |
std::bad_exception |
通常在抛出异常且无法重新抛出时抛出。 |
C++ 中还有许多其他标准异常。
这些异常定义在 exception 头文件中。
要了解更多信息,请访问我们的C++ 标准异常教程。