异常是在程序执行期间发生的意外事件。例如,
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++ 标准异常教程。