异常处理的机制
抛出异常throw
、检查异常try
、捕获异常catch
基本原理:
把需要检测的程序放到 try 块中,把异常处理的程序放到 catch 块中。如果执行一个函数时出现异常,可以不在该函数中立即处理,而是抛出异常信息,传递给它的上一级主调函数,上一级主调函数捕获到这个信息后在进行处理。如果上一级主调函数也不处理,就逐级向上传递,如果传递到了最高一级(main函数)还不处理,最后只能异常终止程序的执行。
C++ 异常处理的机制使异常和处理可以不由同一个函数来完成,这样做的优点是使深层次的函数专注于问题求解,而不必承担处理异常的任务,减轻了深层次函数的负担,而把处理异常的任务集中到某一层次的函数中专门来解决。
异常处理的执行过程
(1)程序流程到达 try 块,然后执行 try 块内的程序块,如果没有引起异常,那么跟在 try 块后的 catch 子句都不执行,程序从最后一个 catch 子句后面的语句继续执行下去;
(2)抛出异常的时候,将暂停当前函数的执行,开始查找匹配的 catch 子句;
(3)首先检查 throw 是否在 try 内部,如果是,检查 catch 子句,看是否其中之一与抛出对象相匹配,如果找到匹配的 catch,就处理异常;如果找不到,就退出当前函数并释放局部对象,然后继续在主调函数中查找;
(4)如果找到匹配的 catch,就处理异常,如果找不到,则退出主调函数,然后继续在调用更高一级的主调函数中查找;
(5)沿着嵌套函数调用链继续向上,直到为异常找到一个 catch 子句,只要找到能够处理异常的 catch 子句,就进入该 catch 子句,并在它的处理程序中继续执行,当 catch 结束时,跳转到该 try 块的最后一个 catch 子句之后的语句继续执行;
(6)如果始终未找到与该被抛异常匹配的 catch 子句,最终 main 函数会结束执行,则运行库函数terminate将被自动调用,terminate函数的默认功能是终止程序,其缺省功能是调用abort终止程序。
例1:
#include <iostream>
using namespace std;
double division(int a, int b)
{
if (b == 0)
{
throw "Division by zero condition!";//抛出异常
}
return (a / b);
}
int main()
{
int x = 50;
int y = 0;
double z = 0;
try {
z = division(x, y);
cout << z << endl;//上一句在执行中如果没有抛出异常才会执行当前这句
}
catch (const char* msg) {
//抛出的异常是const char*类型的,因此接收异常的时候也要是const char*类型的
cout << msg << endl;
}
catch (...) {
//保证以上未列举的其它可能出现的异常均被捕获
cout << "未知异常" << endl;
}
system("pause");
return 0;
}
分析:
异常在division函数中被抛掷,由于division函数本身没有对异常的处理,所以division函数的调用终止,回到main函数对division函数的调用点,又因为该调用点处于一个try子句中,其后接收const char*
类型的catch子句刚好与抛出的异常类型匹配,异常在这里被捕获(这里会有参数传递的过程),然后在该子句中对异常进行处理。
例2:
#include <iostream>
using namespace std;
double division(int a, int b)
{
if (b == 0)
{
throw "Division by zero condition!";//抛出异常
}
return (a / b);
}
void mycatch(int x,int y) {
try {
int z = division(x, y);
cout << z << endl;//上一句在执行中如果没有抛出异常才会执行当前这句
}
catch (...) {
cout << "接受了division的异常 但是我在mycatch中没有处理,再次向上抛出" << endl;
throw;//不带操作数的throw表达式,把异常再次向上抛给主调函数!!
}
}
int main()
{
int x = 50;
int y = 0;
double z = 0;
mycatch(x, y);
system("pause");
return 0;
}
运行结果:
原因是在mycatch中没有对division抛出的异常进行处理,而是又把它往上抛给了主调函数,而在main函数中找不到与被抛掷的异常所匹配的catch子句,最终main函数会结束执行。
解决方法就是在main函数中对该异常进行处理:
注意:用一个不带操作数的throw表达式throw;
可以将当前正在被处理的异常再次向上抛掷,但是这样的表达式只能出现在一个catch子句中或在catch子句内部调用的函数中。并且再次抛掷的异常对象是源异常对象(不是copy副本)。
不报错的程序如下:
#include <iostream>
using namespace std;
double division(int a, int b)
{
if (b == 0)
{
throw "Division by zero condition!";//抛出异常
}
return (a / b);
}
void mycatch(int x,int y) {
try {
int z = division(x, y);
cout << z << endl;//上一句在执行中如果没有抛出异常才会执行当前这句
}
catch (...) {
cout << "接受了division的异常 但是我在mycatch中没有处理,再次向上抛出" << endl;
throw;//不带操作数的throw表达式,把异常再次向上抛给主调函数!!
}
}
int main()
{
int x = 50;
int y = 0;
double z = 0;
try {
mycatch(x, y);
}
catch (...) {
cout << "接受了mycatch向main抛出的异常,在main中完成了对异常的处理" << endl;
cout << "异常如下:Division by zero condition!" << endl;
}
cout<<"That is ok."<<endl;
system("pause");
return 0;
}
栈的解旋
定义:异常被抛出后,从进入try块(与截获异常的catch子句相对应的那个try块)起,到异常被抛出前这期间在栈上构造(且尚未析构)的所有对象都会被自动析构,析构的顺序与构造的顺序相反,这一过程称为解旋。
例:
#include <iostream>
#include <string>
using namespace std;
class MyException {
private:
string message;
public:
MyException(const string&message):message(message){
}
~MyException(){
}
const string&getmessage()const {
return message; }
};
class Demo {
public:
Demo() {
cout << "Demo Constructor" << endl; }
~Demo() {
cout << "Demo Destructor" << endl; }
};
void func()throw(MyException) {
//这里用到了异常接口声明,表示函数func能够且只能够抛掷MyException类或其子类类型的异常!!!
Demo d;
cout << "throw MyException in func()" << endl;
throw MyException("抛出异常");
}
int main()
{
try {
func();
}
catch(MyException&obj){
cout << obj.getmessage() << endl;
}
system("pause");
return 0;
}
可以发现,Demo类的析构函数在”抛出异常”输出前就被自动调用了,体现了栈的解旋。
C++的标准程序库异常处理
今天的文章异常调用栈_c中throw的用法「建议收藏」分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/89061.html