目录
1.异常的概念
异常处理机制允许程序中独⽴开发的部分能够在运⾏时就出现的问题进⾏通信并做出相应的处理,异常使得我们能够将问题的检测与解决问题的过程分开,程序的⼀部分负责检测问题的出现,然后解决问题的任务传递给程序的另⼀部分,检测环节⽆须知道问题的处理模块的所有细节。
C语⾔主要通过错误码的形式处理错误,错误码本质就是对错误信息进⾏分类编号,拿到错误码以后还要去查询错误信息,⽐较⿇烦。C++异常时抛出⼀个对象,这个对象可以包含更全⾯的各种信息。
C++中要使用异常处理时,需要包含头文件exception.
2.异常的抛出和捕捉
程序出现问题时,通过抛出(throw)⼀个对象来引发⼀个异常,该对象的类型以及当前的调⽤链决定了应该由哪个catch的处理代码来处理该异常。
被选中的处理代码是调⽤链中与该对象类型匹配且离抛出异常位置最近的那⼀个。根据抛出对象的类型和内容,程序的抛出异常部分告知异常处理部分到底发⽣了什么错误。
当throw执⾏时,throw后⾯的语句将不再被执⾏。程序的执⾏从throw位置跳到与之匹配的catch模块,catch可能是同⼀函数中的⼀个局部的catch,也可能是调⽤链中另⼀个函数中的catch,控制权从throw位置转移到了catch位置。这⾥还有两个重要的含义:1、沿着调⽤链的函数可能提早退出。2、⼀旦程序开始执⾏异常处理程序,沿着调⽤链创建的对象都将销毁。
抛出异常对象后,会⽣成⼀个异常对象的拷⻉,因为抛出的异常对象可能是⼀个局部对象,所以会⽣成⼀个拷⻉对象,这个拷⻉的对象会在catch⼦句后销毁。(这⾥的处理类似于函数的传值返回)
下面这段程序就是模拟的是当除法运算中除数为0是抛出异常的情况:这里的运行逻辑是在try子句中调用Func()函数,在func()函数中输入两个浮点型的变量,通过这两个变量调用Divide()函数,当除数为0的时候使用throw语句抛出异常,由于整个调用链中只有main函数中有catch子句且throw抛出的对象类型和catch子句接收的参数类型相同,则匹配到该catch子句执行catch子句中的内容。因为在while循环中,执行完之后会再次执行Func()函数。
#include <iostream>
#include <string>
#include <exception>
using namespace std;
//只有一个try -- catch
double Divide(int a, int b)
{
//当 b == 0时抛出异常
if (b == 0)
{
string s("Divide by zero condition");
throw s; //抛出异常往上一层一层找catch
}
else
{
return ((double)a / (double)b);
}
}
void Func()
{
int len, time;
cin >> len >> time;
cout << Divide(len, time) << endl;
}
int main()
{
while (1)
{
try
{
Func();
}
catch (const string& errmsg)
{
cout << errmsg << endl;
}
}
return 0;
}
3.栈展开
抛出异常后,程序暂停当前函数的执⾏,开始寻找与之匹配的catch⼦句,⾸先检查throw本⾝是否在try块内部,如果在则查找匹配的catch语句,如果有匹配的,则跳到catch的地⽅进⾏处理。
如果当前函数中没有try/catch⼦句,或者有try/catch⼦句但是类型不匹配,则退出当前函数,继续在外层调⽤函数链中查找,上述查找的catch过程被称为栈展开。
如果到达main函数,依旧没有找到匹配的catch⼦句,程序会调⽤标准库的 terminate 函数终⽌程序。如果找到匹配的catch⼦句处理后,catch⼦句代码会继续执⾏。
这里还是用上述除法运算的例子,只不过在每一个函数中都添加了try -- catch语句。这里有两个需要注意的点:(1)一个函数模块中可以有多个catch子句,寻找catch子句时从上至下依次进行类型匹配。(2)catch子句进行类型匹配的时候不支持隐式类型转换,下列的例子中就不支持从string转换为char*对象。
下列程序中的异常是在Divide()中进行抛出,但是通过栈展开和catch语句的匹配,最终是执行main()函数中catch (const string& errmsg)子句对应的内容。