C++异常处理


异常

异常处理机制增加了程序的清晰性和可读性,让程序员编写清晰、健壮、容错能力更强的程序。


优势

  • 异常代码和主程序代码分离
  • 异常检测、异常恢复
  • 处理库函数引起的异常
  • 无法处理的问题,适当的方式退出
  • 动态调用动态调用链中有序地传播异常处理

传统异常处理

执行任务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语言中异常处理

使用setjmplongjmp进行异常捕获与处理:

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;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值