异常处理(异常类exception)
本文参考各种网文并加入个人理解
0、防御式编程
主要的防御式编程手段有断言(C语言)、错误处理技术、异常(C++)、隔离
传送门
一、总览
- 异常是一种“程序控制机制(异常处理机制)”,与“函数机制”独立和互补
- 函数:是一种以栈结构展开的上下函数衔接的程序控制系统
- 异常:是另一种控制结构,它依附于栈结构,却可以同时设置多个异常类型作为网捕条件,从而以类型匹配在栈机制中跳跃回馈.
- 异常处理设计出来之后,却发现在“错误处理”方面获得了最大的好处
- 异常设计目的:栈机制是一种高度节律性控制机制,面向对象编程却要求对象之间有方向、有目的的控制传动。从一开始,异常就是冲着改变程序控制结构,以适应面向对象程序更有效地工作这个主题,而不是仅为了进行错误处理。
二、2种错误处理机制思想对比
1)传统错误处理机制
通过函数返回值来处理错误。
2)C++的异常处理机制
- 1)C++的异常处理机制使得异常的引发和异常的处理不必在同一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理。上层调用者可以再适当的位置设计对不同类型异常的处理。
- 2)异常是专门针对抽象编程中的一系列错误处理的,C++中不能借助函数机制,因为栈结构的本质是先进后出,依次访问,无法进行跳跃,但错误处理的特征却是遇到错误信息就想要转到若干级之上进行重新尝试,如下图
- 3)异常超脱于函数机制,决定了其对函数的跨越式回跳(感觉想goto,底层应该也会影响程序流水线)。
- 4)异常跨越函数
三、异常基本语法
1)抛掷(扔出去),throw
2)捕获并且处理异常,try和catch
1)若有异常则通过throw操作创建一个异常对象并抛掷。
2)将可能抛出异常的程序段嵌在try块之中。控制通过正常的顺序执行到达try语句,然后执行try块内的保护段。
- 如果在保护段执行期间没有引起异常,那么跟在try块后的catch子句就不执行。程序从try块后跟随的最后一个catch子句后面的语句继续执行下去。
- catch子句按其在try块后出现的顺序被检查。匹配的catch子句将捕获并处理异常(或继续抛掷异常,处理不了的异常,可以在catch的最后一个分支,使用throw语法,向上扔。(因为可以一直抛掷,抛掷到最上面是操作系统来处理!!!)—— 如果匹配的处理器未找到,则运行函数terminate将被自动调用,其缺省功能是调用abort终止程序。
- 系统的最后一道防线——激发terminate函数,该函数调用引起运行终止的abort函数.
最后一道防线的函数可以由程序员设置.从而规定其终止前的行为.
3)优点:构造函数没有返回类型,无法通过返回值来报告运行状态,所以只通过一种非函数机制的途径,即异常机制,来解决构造函数的出错问题。
4)异常机制与函数机制互不干涉,但捕捉的方式是基于类型匹配。捕捉相当于函数返回类型的匹配,而不是函数参数的匹配,所以捕捉不用考虑一个抛掷中的多种数据类型匹配问题
- catch代码块必须出现在try后,并且在try块后可以出现多个catch代码块,以捕捉各种不同类型的抛掷。异常机制是基于这样的原理:程序运行实质上是数据实体在做一些操作,因此发生异常现象的地方,一定是某个实体出了差错,该实体所对应的数据类型便作为抛掷和捕捉的依据。
- PS:catch里面其实,不仅可以用int啥的,而且还能接收那个异常(注意理解),比如1、样例代码1中catch int e和样例代码3只管int
5)异常捕捉严格按照类型匹配!!(请看,代码样例4)
异常捕捉的类型匹配之苛刻程度可以和模板的类型匹配媲美,它不允许相容类型的隐式转换,比如,抛掷char类型用int型就捕捉不到.
1、代码样例
#include<bits/stdc++.h> using namespace std; int divide(int x, int y ) { if (0 == y) { //抛掷出x throw x; } return x/y; } int main() { try { cout << "8/2 = " << divide(8, 2) << endl; cout << "999/0 =" << divide(999, 0) << endl; } catch (int e) { cout << e << " is divided by zero!" << endl; } catch(...) { cout << "未知异常" << endl; } cout << "最后一行" << endl; return 0; } //8/2 = 4 //999 is divided by zero! //最后一行
2、代码样例2
#include<bits/stdc++.h> using namespace std; class A { }; class B { }; void f() { A a; throw a; } void g() { try { f(); } catch(B) { //类型匹配不到,继续往上抛掷,直到terminate处理 cout<<"exception B\n"; } } //void MyTerminate() //{ // cout<<"自定义的系统最后一道防线处理方式"<<endl; //} int main() { // set_terminate(MyTerminate); g(); return 0; }
现象:
编译通过,运行时候发现terminate处理异常了!
terminate called after throwing an instance of 'A' This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.
原因:
throw A类型的a,一层层穿透了我们的,函数f(),g(),main()抵达了系统最终防线——terminate
PS:修改系统默认行为:
- 可以通过set_terminate函数修改捕捉不住异常的默认处理器,从而使得发生捉不住异常时,被自定义函数处理:
- void myTerminate(){cout<<“HereIsMyTerminate\n”;}
- set_terminate(myTerminate);
- set_terminate函数在头文件exception中声明,参数为函数指针
void(*)()
效果,将前面的注释去掉,运行效果
自定义的系统最后一道防线处理方式 This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.
3、代码样例3
#include<bits/stdc++.h> using namespace std; class A{}; class B{}; int main() { try { int j; double d; char str[20] = "Hello"; cout<<"Please input a exception number: "; int a; cin>>a; switch(a) { case 1: throw d; case 2: throw j; case 3: throw str; case 4: throw A(); case 5: throw B(); default: cout<<"No throws here.\n"; } } catch(int) { cout<<"int类型异常.\n"; } catch(double) { cout<<"double类型异常.\n"; } catch(char*) { cout<<"char*类型异常.\n"; } catch(A) { cout<<"class A 类型异常.\n"; } catch(B) { cout<<"class B 类型异常.\n"; } return 0; }
4、代码样例4
下列代码不会输出catch处理中的东西,也不会输出最后的打印 !
因为出现异常后提示退出
#include<bits/stdc++.h> using namespace std; int main() { try { throw 'H'; } catch(int) { cout<<"我只能捕获Int类型错误\n"; } cout<<"我也很想运行,但是哪个异常没有被捕获"<<endl; return 0; } //terminate called after throwing an instance of 'char' // //This application has requested the Runtime to terminate it in an unusual way. //Please contact the application's support team for more information.