11. 异常
abort() 程序异常终止
#include<cstdlib>
if(a == -b) // 判断可能出现的异常
cout<< "Error!\n";
abort(); // 调用函数,发送错误消息,终止程序。
abort()向标准错误流发送消息abnormal program termination(程序异常终止),然后终止程序。
try-throw-catch 异常处理机制
基本类型作为异常类型
try // 标识其中特定异常可能被激活
{
...;
if(异常) // 检测异常
{
throw 抛出的异常; // 抛出异常
}
}
catch (异常类型) // 用以处理前面try块对应异常类型的异常
{
...; // 异常处理
}
- 如果try中某条语句导致异常被引发,则后面的catch块将对异常进行处理,如果是try块外引发的异常将不会被处理
- 执行throw语句类似于执行函数返回,但不同的是其不是返回给调用程序的下一条语句,而是后退到包含try块的函数, 然后找到try块后面对应的catch块,进行异常处理。
- 执行完try,如果没有引发任何异常,将跳过try后面的catch块。
- 当引发异常后,只有对应类型的异常处理块会被执行。
- 没有操作数的throw表达式将重新引发当前正在处理的异常。
- 可以使用省略号代表任何异常
catch(...),这代表处理任何异常
// error3.cpp
#include <iostream>
using namespace std;
double hmean(double a, double b);
int main()
{
double x, y, z;
cout << "Enter two number: ";
while(cin >> x >> y)
{
try // try块,标识可能会出现异常的地方
{
z = hmean(x, y);
}
catch (const char *s) // 用以处理try块内throw抛出的字符串类异常
{
cout << s << endl;
cout << "Enter a new pair numbers: ";
continue;
}
cout << "Harmonic mean of " << x " and " << y
<< " is " << z << endl;
cout << "Enter new pair <q tu quit>: ";
}
cout << "Bye!\n";
return 0;
}
double hmean(double a, double b)
{
if(a == -b)
throw "bad hmean() arguments: a = -b not allowed!"; // 抛出异常
return 2.0 * a *b /(a +b);
}
当-10和10 被传递给hmean()后,if导致hmean()引发异常。这将终止hmean()的运行,程序向后搜索发现,heam()是从main()内的try块中调用的,因此程序开始查找main()内此try后的catch异常处理程序。直到发现程序中唯一的catch块的参数为char*,这与引发的异常类型匹配。程序将字符串"bad hmean() arguments: a = -b not allowed!"传递给变量s,然后执行catch块内程序。
将对象作为异常类型
-
将对象作为异常类型的优点:
- 用以区别不同异常。
- 对象可以携带信息,用以指出更多的信息。
class bad_hmean
{
private:
double v1;
double v2;
public:
bad_hmean(int a = 0, int b = 0) : v1(a), v2(b) {}
void mesg();
};
inline void bad_hmean::mesg()
{
cout << "hmean(" << v1 << ", " << v2 << "): "
<< "invalid arguments: a = -b\n";
}
// ...
try
{
if(a == -b)
throw bad_hmean(a, b); // 这将会调用构造函数bad_hmean()以初始化对象,使其存储参数值。
}
catch (bad_hmean & bh) // 将交由此catch块处理
{
...;
}
catch (bad_gmean & bg)
{
...;
}
栈解退
当try块没有直接调用了引发throw的函数,而是调用了对引发异常的函数进行调用的函数,则程序将从引发异常的函数跳到包含try和catch块的函数中。这涉及到了栈解退。
-
函数调用与返回 return
当函数被调用时,将进行函数栈帧的压栈。程序将调用函数的指令的地址(返回地址)放到栈中。当被调用函数执行完后,程序将使用该地址来确定从调用函数的哪里开始继续执行,将返回调用函数后面的第一条语句。除调用函数的指令的地址外,传入参数、被调用函数内部创建的局部变量也将随之被压栈,如果被调用函数又调用了另一个函数,那么这个函数的信息也被压栈,依此类推。当函数执行结束后,程序将跳到该函数被调时存储的地址处。同时栈顶的元素被释放,此过程中自动变量也将被释放,这个函数返回的过程将销毁栈帧。这是函数的调用与返回过程,栈帧将逐级创建,逐级释放。
-
栈解退 throw
当函数由于出现异常而非正常返回时,程序也将释放栈中的内存,但不会在释放栈的第一个返回地址后停止,而是继续释放栈,直到找到一个位于try块中的地址,随后程序将跳转到位于try尾的相应类型的异常处理程序catch,而不是函数调用后面的第一条语句。这个过程叫做栈解退。