文章目录
异常
异常处理机制增加了程序的清晰性和可读性,让程序员编写清晰、健壮、容错能力更强的程序。
优势
- 异常代码和主程序代码分离
- 异常检测、异常恢复
- 处理库函数引起的异常
- 无法处理的问题,适当的方式退出
- 动态调用动态调用链中有序地传播异常处理
传统异常处理
执行任务1
if 任务1未能被正确执行
执行错误处理程序
执行任务2
if 任务2未能正确执行
执行错误处理程序
执行任务3
try{
//try程序块
if err1 throw xx1
……
if err2 throw xx2
……
if errn throw xxn
}
catch(type1 arg){……} //异常类型1错误处理
catch(type2 arg){……} //异常类型2错误处理
catch(typem arg){……} //异常类型m错误处理
C语言中异常处理
使用setjmp
和longjmp
进行异常捕获与处理:
setjmp和longjmp是非局部跳转,类似goto跳转作用,但是goto语句具有局限性,只能在局部进行跳转,当需要跳转到非一个函数内的地方时就需要用到setjmp和longjmp。
setjmp函数用于保存程序的运行时的堆栈环境,接下来的其它地方,
你可以通过调用longjmp函数来恢复先前被保存的程序堆栈环境。
int setjmp(jmp_buf env);
// 将当前上下文保存在jmp_buf结构体中(入栈),并返回0
void longjmp(jmp_buf env,int val);
// 恢复先前被保存的程序堆栈环境
//==================================================
#include <stdio.h>
#include <setjmp.h><br>
jmp_buf env; // 必然要使用全局变量jmp_buf env, 保存堆栈信息
double divide(double a,double b)
{
const double delta = 0.00000000001; //由于浮点数不精确,所以需要定义个很小的数
if(!((-delta<b)&&(b<delta))){
return a/b ;
}
else{
longjmp(env,1); //直接跳转到23行,ret=setjmp(env)代码处,并返回异常值(1)
return 0;
}
}
int main( )
{
int ret;
ret = setjmp(env); //手动调用 setjmp(),将返回正常值(0),
if(!ret){ //正常操作
printf("5/0=%lf\n",divide(5,0));
}
else if(ret==1){ //异常操作
printf("ERR\n");
}
return 0;
}
"缺点:"
// 若需要两个函数之间跳转,必然要使用全局变量jmp_buf env
// 跳转使得代码可读性降低
使用setjmp设置一个跳转点,然后在程序其他地方调用longjmp跳转到该点(抛出异常).
异常捕获
throw(参数列表) 与 catch(参数列表) 的参数列表一致的时候才可以捕获到异常
异常的数据类型不会进行默认转换,只会进行精确的匹配。
catch中类型比较:
C++将按catch块出现的次序,用异常的数据类型与每个catch参数表中指定的数据类型相比较,如果两者类型相同,就执行该catch块,同时还将把异常的值传递给catch块中的形参arg(如果该块有arg形参)。只要有一个catch块捕获了异常,其余catch块都将被忽略。、
无类型匹配:
catch能够匹配该异常,C++将调用系统默认的异常处理程序处理该异常,其通常做法是直接终止该程序的运行
#include<iostream>
using namespace std;
void main(){
cout<<"1--befroe try block..."<<endl;
try{
cout<<"2--Inside try block..."<<endl;
throw 10;
cout<<"3--After throw ...."<<endl;
}
catch(int i) {
cout<<"4--In catch block (int )"<<i<<endl;
}
catch(char * s) {
cout<<"5--In catch block2 (char* )"<<s<<endl;
}
cout<<"6--After Catch...";
}
异常限定
但是我实际测试的时候发现这个异常限定好像没有用!
限制异常的方法
1、当一个函数声明中不带任何异常描述时,它可以抛出任何异常。例如:
int f(int,char); //函数f可以抛出任何异常
2、在函数声明的后面添加一个throw参数表,在其中指定函数可以抛出的异常类型。例如:
int g(int,char) throw(int,char); //只允许抛出int和char异常。
3、指定throw限制表为不包括任何类型的空表,不允许函数抛出任何异常。如:
int h(int,char) throw(); //不允许抛出任何异常
#include<iostream>
using namespace std;
void Errhandler(int n)throw(int,char,double) {
if(n==1) throw n;
if(n==2) throw 'x';
if(n==3) throw 1.1;
}
void main(){
cout<<"Before Errhander..."<<endl;
try{
Errhandler(1);
}
catch(int i){ cout<<"catch an integer..."<<endl;}
catch(char c){cout<<"catch an char..."<<endl;}
catch(double d){cout<<"catch an double..."<<endl;}
所有异常捕获
catch(…) {
//异常处理代码
}
#include<iostream>
using namespace std;
void Errhandler(int n)throw(){
try{
if(n==1) throw n;
if(n==2) throw "dx";
if(n==3) throw 1.1;
}
catch(…){cout<<"catch all type exception"<<endl;}
}
void main(){
Errhandler(1);
Errhandler(2);
Errhandler(3);
}
异常再次抛出
catch块无法处理捕获的异常,它可以将该异常再次抛出,使异常能够在恰当的地方被处理。
要在catch块中再次抛出同一异常,只需在该catch块中添加不带任何参数的throw语句即可
#include<iostream>
using namespace std;
void Errhandler(int n)throw()
{
try{
if(n==1) throw n;
cout<<"all is ok..."<<endl;
}
catch(int n){
cout<<"catch an int exception inside..."<<n<<endl;
throw; //再次抛出本catch捕获的异常
}
}
void main(){
try{
Errhandler(1);
}
catch(int x){
cout<<"catch int an exception in main..."<<x<<endl;
}
cout<<"....End..."<<endl;
}
异常嵌套
#include<iostream>
using namespace std;
void fc(){
try{
throw "help...";
}
catch(int x){
cout<<"in fc..int hanlder"<<endl;
}
try{
cout<<"no error handle..."<<endl;
}
catch(char *px){
cout<<"in fc..char* hanlder"<<endl;
}
}
void fb(){
int *q=new int[10];
try{
fc();
cout<<"return form fc()"<<endl;
}
catch(…){
delete []q;
throw;
}
}
void fa()
{
char *p=new char[10];
try{
fb();
cout<<"return from fb()"<<endl;
}
catch(…){
delete []p;
throw;
}
}
void main(){
try{
fa();
cout<<"return from fa"<<endl;
}
catch(…){cout<<"in main"<<endl;}
cout<<"End"<<endl;
}
构造函数中的异常
#include<iostream>
using namespace std;
class A{
int a;
public:
A(int i=0):a(i){}
~A(){cout<<"in A destructor..."<<endl;}
};
class B{
A obj[3];
double *pb[10];
public:
B(int k)
{
cout<<"int B constructor..."<<endl;
for (int i=0;i<10;i++)
{ //类B的构造函数进行了自由存储空间的过量申请,最后造成内存资源耗尽产生异常
pb[i]=new double[20000000];
if(pb[i]==0)
throw i;
else
cout<<"Allocated 20000000 doubles in pb["<<i<<"]"<<endl;
}
}
};
void main(){
try{
// 当构造函数出现错误时就抛出异常,外部函数可以在构造函数之外捕获并处理该异常
B b(2);
}
catch(int e){
cout<<"catch an exception when allocated pb["<<e<<"]"<<endl;
}
}
异常类
- 利用面向对象的多态性进行异常捕获
#include <iostream>
using namespace std;
class BasicException{
public:
virtual char* Where(){return "BasicException...";}
};
void main(){
try{
//..... 程序代码
throw FileSysException();
}
catch(BasicException &p)
{
cout<<p.Where()<<endl;
}
try{
//..... 程序代码
throw DiskNotFound();
}
catch(BasicException &p)
{
cout<<p.Where()<<endl;
}
}