异常处理
什么是异常处理
在程序中可能会出现各种各样的异常,比如出现了错误,常见错误主要分为语法错误和编译错误,有的错误编译器是可以检查出来的,有的错误编译器检查不出来,;又比如违背正常情况,比如要求输入小写字母,但是实际输入大写的或者数字等,这种错误编译器一般不会检查出来。
在设计程序之前,应当分析各种可能出现的异常情况,并设定相应的识别、处理方式,这就是异常处理的任务
比如如果某个数除以0,这就是一个异常,要根据除数为0的这种异常情况进行一个判断和处理(比如报错并结束程序)
当然,也可以通过断言等方式来处理一些错误,当assert中的判别式结果为false的时候,就直接异常退出程序
C++中如何实现异常处理
C++的异常处理机制是由三个部分组成的:检查(try)、抛出(throw)、捕捉(catch)
"异常情况“是设计者人为认定判定的:如果想要检查一段代码,在这个代码段里面出现了我们不想要的异常情况,就需要抛出”这是一个异常情况“的信号,然后由相应的捕捉代码进行捕捉,一旦捕捉到抛出的异常,就会进入到用来处理这个异常的代码。
跟assert相比,异常处理机制显然更为温和,不会直接断死代码直接退出程序
throw语句
形式:
throw 表达式
throw关键字抛出的表达式是什么样的数据由设计者自己定,可以是常规的变量,也可以是一串字符,也可以是自定义的变量
throw的关键在于表达式的结果的类型,而不是表达式的具体的值,但还是会将表达式作为“异常信号/信息”(异常)整个抛出去,包括值
一旦throw抛出异常,就会在进行异常处理之后,立即跳出throw所在的函数,回到上一级函数(比如main函数)
try-catch结构
try关键字负责划定需要检查的代码区域,在区域内的代码段会接受检查,try和被检查的代码段被合称为try块
catch关键字用于接收throw抛出的异常,并针对异常进行相应处理,catch和被检查的代码段被合称为catch块
形式:
try
{
被检查的代码段
}catch(异常信息类型 变量名·一般情况下不写变量名·){
处理异常的代码段
}
注意
- 如果throw的表达式是类的话,可以不用单独实例化出一个对象,直接使用匿名对象,因为有的时候会定义一些空类来专门作为异常信息用,没必要为了异常信息实例化一个对象
class outofsize{};//这是一个空类,专门用来当异常信息的
......
throw outofsize();//使用的outofsize的匿名对象,格式是"类名"+"()"
try
{}
catch(outofsize)
{}
-
被检查的代码段必须放在try里,否则不起作用 -
try-catch结构是一个整体,catch块必须紧跟try块,catch不能脱离try单独使用,且两者之间不能夹杂其他语句;但是try块是可以单独使用的,即只检查不处理,catch块在另一个函数里面 -
花括号不能省略 -
由于一段代码中可能存在多种错误情况,所以允许一个try块后面接多个catch块,以便匹配不同的异常信息
try
{}
catch()
{}
catch()
{}
- catch也只检查异常信息的类型,而不检查异常信息的值,根据异常信息的类型匹配相应的catch块,无论doule类型的值是多少,都只会匹配catch(double);所以一般的catch写法括号内只有异常信息类型
catch( double )
{}
这里发现,在函数的定义的时候也可以只写类型,不写变量,因为参数从左到右依次传入函数
? catch括号内还有另一种写法,即除了指定类型之外还指定变量名
catch(double d)
{
cout << d << endl;
}
? 这样catch在捕捉异常信息的时候,除了捕捉变量类型,还会捕捉变量的值,这样就可以在catch中使用这个值(当然,是拷贝的)
-
如果catch括号内没有写变量类型,而是写了catch(…),这表示这个catch块可以接受任何类型的异常信息 -
throw语句和try-catch块在同一个函数中,也可以不在同一个函数中。throw抛出异常信息之后,会先在本函数内让catch捕捉,如果本函数没有try-catch或者没有可以匹配的catch,则跳出本函数到上一个函数,直到找到最近的可匹配的catch -
某些情况下,throw后面可以不加表达式,比如如果在catch块里有throw,则catch里的throw会将catch捕捉到的catch外的throw抛出的异常原样抛出,表示“本级函数的catch不处理这个异常,给上一级的catch块处理”
catch(int)
{ //其他语句
throw; //将以捕获的异常信息再次原样抛出
}
- 如果throw抛出的异常到最后都没有catch与之匹配,系统会调用一个系统函数terminate终止程序进行
几个例子
#include<iostream>
#include<cmath>
using namespace std;
double triangle(double,double,double);
int main()
{
double a,b,c;
cin>>a>>b>>c;
try
{
while(a>0&&b>0&&a>0)
{
cout<<triangle(a,b,c)<<endl; //try里包含了要被检查的函数
cin>>a>>b>>c;
}
}catch(double)
{
cout << "a=" << a << ",b=" << b << ",c=" << c << ",that is not a triangle" <<endl;
}
cout<< "end" <<endl;
return 0;
}
double triangle(double a,double b,double c)
{
double s = ( a+b+c )/2;
if( a + b <= c || b + c <= a || a + c <= b )
throw a;
return sqrt( s + (s-a) * (s-b) * (s-c) ); //如果在上面的throw中抛出了异常信息,则直接跳到上一级函数,这一行代码就不会生效
}
#include <iostream>
using namespace std;
double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!"; //表达式的类型本质是上是字符串,也就是char指针
}
return (a/b);
}
int main ()
{
int x = 50;
int y = 0;
double z = 0;
try
{
z = division(x, y);
cout << z << endl;
}catch (const char* msg) //既有变量类型又有变量名,就可以在下面使用捕捉到的异常信息
{
cerr << msg << endl;
}
return 0;
}
#include<iostream>
#define SIZE 10
using namespace std;
class outOfSize {};//这里定义了一个空outOfSize类,只用作异常信号,不用与其他作用
class illegalType {};//同上
void addWord(int&, char*, char);
int main()
{
char a[SIZE];
char b;
int x = 0;
try
{
while (1)
{
cout << "Please keyonte a~z"<<endl;
cin >> b;
addWord(x, a, b);
}
}
catch (outOfSize)
{
cout << "Outofsize!" << endl;
}
catch (illegalType)
{
cout << "IllegalType!" << endl;
}
return 0;
}
void addWord(int& x, char* a, char b)
{
if (x >= SIZE)
throw outOfSize();//抛出的是outOfSize类的匿名对象
if (b < 'a' || b>'z')
throw illegalType();
a[x] = b;
x++;
}
结束
That’s all, thanks for reading!💐
|