在一个小的程序中,可以用比较简单的方法处理异常,例如用if语句判别除数是否为0,如果是0,则输出一个出错信息。但是在一个大的系统中,包含很多模块,每个模块又包含许多类和函数,函数之间又互相调用,比较复杂。如果在每一个函数中都设置处理异常的程序段,会使程序过于庞大和复杂。因此c++采用的办法是:如果在执行一个函数过程中出现异常,可以不在本函数中立即处理,而是发出一个信息,传给他的上一级(即调用他的函数),他的上机捕捉到这个信息后进行处理。如果上一级的函数也不能处理,就再传给上一级,如此逐级上送,如果还是不存在处理该异常的catch子句,程序的运行就要跳转到名为terminate的标准库函数,该函数在exception头文件中定义。这个标准库函数的行为依赖于系统,通常情况下,它的执行将导致程序非正常退出。
(一)基本语法结构
一般的catch出现的形式是:
try{}
catch(except1&){}
catch(except2&){}
catch(...){} //接受所有异常
一般都写成引用(except1&),原因很简单,效率。
简单的示例:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string ex = "this is a exception";
try
{
cout<<"before throw"<<endl;
throw ex;
cout<<"after throw"<<endl;
}
catch(string &e)
{
cout<<e<<endl;
}
cout<<"end"<<endl;//
}
注意,catch中抛出string类型的异常后立即跳出此函数,随后被catch捕捉,执行catch处理后,继续执行catch后的语句,输出"end"。
(二)多个catch捕捉
一个try-catch结构中只能有一个rtry块,但是却可以有多个catch块,以便与不同的异常信息匹配。
int main()
{
try
{
cout << "在 try block 中, 准备抛出一个异常." << endl;
//这里抛出一个异常(其中异常对象的数据类型是char*)
char* p=0;
throw p;
}
//catch( char* value )
//注意这里catch语句
catch(...)
{
cout << "在 catch(…) block 中, char*类型的异常对象也被处理" << endl;
}
}
catch还有另外一种写法,即除了指定类型名之外,还可以指定变量名,如catch(double d) { cout<<"throw "<<d<<endl; }catch(...)删节号...代表他可以捕捉到任何类型的异常信息,通常放在try-catch结构的最后。
(三)嵌套的try-catch结构
int main()
{
try
{
//这里是嵌套的trycatch结构块
try
{
cout << "在 try block 中, 准备抛出一个int数据类型的异常." << endl;
throw 1;
}
catch( double )
{
cout << "在 catch block 中, double数据类型处理异常错误。"<< endl;
}
cout << "在 try block 中, 准备抛出一个int数据类型的异常." << endl;
throw 0.5;
}
catch( int )
{
cout << "在 catch block 中, int数据类型处理异常错误。"<< endl;
}
return 0;
}
抛出int异常后,要到上一层函数调用中才找到匹配的catch,因此只会输出最后一个catch中的处理数据。
[特别提示]:在C++标准中规定,可以在程序任何地方throw一个异常对象, 并不要求一定只能是在受到try block监控保护的作用域中才能抛出异常, 如果在程序中出现了抛出的找不到对应catch block的异常对象时,C++标准中规定要求系统必须执行terminate()来终止程序。 因此这个例程是可以编译通过的,但运行时却会异常终止。这往往给软件 系统带来了不安全性。与此形成对比的是java中提供的异常处理模型却是不允许出现这样的找不到对应catch block的异常对象,它在编译时就给出错误 ,所以java中提供的异常处理模型往往比C++要更完善。
try-catch的问题解决了,我们再来看一下我在c++ primer的语句这一章找到的两个知识点:
1.悬垂else问题
int main()
{
int a=0,b=0;
if(a<=b)
if(a==b)
a++;
else
b++;
cout<<a<<" "<<b<<endl;
return 0;
}
这个程序表明了所有语言的if语句都普遍存在着潜在的二义性。这种情况往往成为悬垂else问题;上述代码中,看缩进的用法表明else应与外层的if语句相匹配。然而,C++中解决悬垂else问题的二义性的办法 是通过将else匹配给最后出现的尚未匹配的if子句来解决。
所以相当于第一个if的处理是空语句。
2.简洁就是美
while语句内部数组复制的简洁代码
int arr1[10];
int *source=arr1;
size_t sz=sizeof(arr1)/sizeof(*arr1);
int *dest=new int[sz];
while(source!=arr1+sz)
*dest++=*source++; //简洁,相当于
/*
*dest=*source;
++dest;
++source;
*/
可以在for语句的init-statement中定义多个对象,但是不管怎样,该处只能出现一个语句,因此所有的对象必须具有相同的一般类型
for(int ival=0,*pi=ia,&ri=val;ival!=size;++ival,++pi,++ri)
{} //必须都为int类型