C++和C中常见的异常处理

程序中的错误分为编译时的错误和运行时的错误。编译时的错误主要是语法错误,比如:句尾没有加分号,括号不匹配,关键字错误等,这类错误比较容易修改,因为编译系统会指出错误在第几行,什么错误。而运行时的错误则不容易修改,因为其中的错误是不可预料的,或者可以预料但无法避免的,比如内存空间不够,或者在调用函数时,出现数组越界等错误。如果对于这些错误没有采取有效的防范措施,那么往往会得不到正确的运行结果,程序不正常终止或严重的会出现死机现象。我们把程序运行时的错误统称为异常,对异常处理称为异常处理。C++中所提供的异常处理机制结构清晰,在一定程度上可以保证程序的健壮性。


一、C++中异常处理

C++中处理异常的过程是这样的:在执行程序发生异常,可以不在本函数中处理,而是抛出一个错误信息,把它传递给上一级的函数来解决,上一级解决不了,再传给其上一级,由其上一级处理。如此逐级上传,直到最高一级还无法处理的话,运行系统会自动调用系统函数terminate,由它调用abort终止程序。这样的异常处理方法使得异常引发和处理机制分离,而不在同一个函数中处理。这使得底层函数只需要解决实际的任务,而不必过多考虑对异常的处理,而把异常处理的任务交给上一层函数去处理。


C++的异常处理机制有3部分组成:try(检查),throw(抛出),catch(捕获)。把需要检查的语句放在try模块中,检查语句发生错误,throw抛出异常,发出错误信息,由catch来捕获异常信息,并加以处理。一般throw抛出的异常要和catch所捕获的异常类型所匹配。异常处理的一般格式为:

try{
	被检查语句
    throw 异常
}
catch(异常类型1){
	进行异常处理的语句1
}
catch(异常类型2){
    进行异常处理的语句2
}
...
如果在执行try语句模块时,没有发生异常,则catch语句块不起作用,流程转到其后的语句继续执行。


对异常处理注意几点:

(1)try和catch块中必须要用花括号括起来,即使花括号内只有一个语句也不能省略花括号;

(2)try和catch必须成对出现,一个try_catch结果中只能有一个try块,但可以有多个catch块,以便与不同的异常信息匹配;

(3)如果在catch块中没有指定异常信息的类型,而用删节号"...",则表示它可以捕获任何类型的异常信息;

(4)如果throw不包括任何表达式,表示它把当前正在处理的异常信息再次抛出,传给其上一层的catch来处理;

(5)C++中一旦抛出一个异常,如果程序没有任何的捕获,那么系统将会自动调用一个系统函数terminate,由它调用abort终止程序;

(6)遇到异常,不会执行之后操作。除非有finally语句,执行return及以后的操作,然后执行finally语句块,若finally语句有return,则覆盖之前的return。


举个栗子:

#include 
using namespace std;

template
T Div(T x,T y){
	if(y==0)
		throw y;
	return x/y;
}

int main(){
	int i_x=5, i_y=1;
	double d_x=5.5, d_y=0.0;
	try{
		cout << i_x << "/" << i_y << "=" << Div(i_x,i_y) << endl;
		cout << d_x << "/" << d_y << "=" << Div(d_x,d_y) << endl;
		cout << "异常后还能执行" << endl;
	}
	catch(...){		//捕获任何异常
		try{
			cout << "捕获任意异常类型" << endl;
			throw;	//抛出当前处理异常信息给上一层catch
		}
		catch(int){
			cout << "除数为0!" << endl;
		}
		catch(double){
			cout << "除数为0.0!" << endl;
		}
	}
	system("pause");
	return 0;
}

输出为:



调用函数Div(x,y)时发生异常,由函数Div中的语句"throw y"抛出异常,并不在往下执行cout << "异常后还能执行" << endl;,接着catch捕获...任意类型的异常,然后throw把当前异常继续抛出,传给上一层catch,直到catch(double)捕获到,最后直接执行"return 0"。


二、C语言的中异常处理

1.使用标准C库提供了abort()和exit()两个函数,它们可以强行终止程序的运行,其声明处于头文件中。

2.使用assert(断言)宏调用,位于头文件中,当程序出错时,就会引发一个abort()。


使用exit()函数进行异常终止:

#include 
#include 
double diva(double num1,double num2)         //两数相除函数 
{
    double re;
    re=num1/num2;
    return re;
}
int main()
{
   double a,b,result;
   printf("请输入第一个数字:");
   scanf("%lf",&a);
   printf("请输入第二个数字:");
   scanf("%lf",&b);
   if(0==b)                                //如果除数为0终止程序 
     exit(EXIT_FAILURE);
   result=diva(a,b);
   printf("相除的结果是: %.2lf\n",result);    
   return 0;
}
exit的函数原型:void exit(int)由此,我们也可以知道EXIT_FAILURE宏应该是一个整数,exit()函数的传递参数是两个宏,一个是刚才看到的EXIT_FAILURE,还有一个是EXIT_SUCCESS从字面就可以看出一个是出错后强制终止程序,而一个是程序正常结束。他们的定义是:
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1

当出现异常的时候,程序是终止了,但是我们并没有捕获到异常信息。


使用exit()函数进行异常终止:

assert()是一个调试程序时经常使用的宏,切记,它不是一个函数,在程序运行时它计算括号内的表达式,如果表达式为FALSE  (0),  程序将报告错误,并终止执行。如果表达式不为0,则继续执行后面的语句。这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。   

另外需要注意的是:assert只有在Debug版本中才有效,如果编译为Release版本则被忽略。
我们就前面的问题,使用assert断言进行异常终止操作:构造可能出现出错的断言表达式:assert(number!=0)这样,当除数为0的时候,表达式就为false,程序报告错误,并终止执行。

代码如下:

#include 
#include 
double diva(double num1,double num2)         //两数相除函数 
{
    double re;
    re=num1/num2;
    return re;
}
int main()
{
   printf("请输入第一个数字:");
   scanf("%lf",&a);
   printf("请输入第二个数字:");
   scanf("%lf",&b);
   assert(0!=b);                                //构造断言表达式,捕获预期异常错误
   result=diva(a,b);
   printf("相除的结果是: %.2lf\n",result);    
   return 0;
}



你可能感兴趣的:(编程基础)