首页 > 学院 > 开发设计 > 正文

【c++】异常

2019-11-14 11:21:28
字体:
来源:转载
供稿:网友

程序有时会遇到运行阶段的错误,比如打开一个不存在的文件,请求过多内存,接受一个不能使用值……通常程序员会避免这样的的以外,而c++提供了一种功能清大的而灵活的工具——异常。

例如下面这个示例

int main(int argc, char** argv) { int x=19; int y=-x; cout<<(2*x*y/(x+y)); return 0;}

上面是输出两个数的调和平均数(两个数的倒数的平均数的倒数)

对于上面程序而言编译器可能输出一个表示无穷大的浮点数值,cout将这个值输出为Inf,inf,INF(Infinitely),也可能直接崩溃(dev c++5.11)。

解决方法 1

#include <iostream>#include<cstdlib>//for abort()using std::cout;using std::cin;using std::endl;double harmonic(double a,double b);int main(int argc, char** argv) { int x=0; int y=0; cout<<"enter two number"; while(cin>>x>>y) { int z=harmonic(x,y); cout<<"harmonic mean of "<<x <<" and "<<y<<" is "<<z<<endl; cout<<"enter next set of number<q to quit>:"; } cout<<"Bye!/n"; return 0;}double harmonic(double a,double b){ if(a==-b){ cout<<"untenable argument to harmonic/n"; std::abort(); } return 2.0*a*b/(a+b);}

abort 是位于头文件cstdlib 中,其典型实现是向标准错误流发送 abnormal PRogram termination(程序终止)然后终止程序,还返回一个随实现而异的值,告诉父进程处理失败。abort刷不刷新文件缓冲区(用于存储读写到文件中的数据的内存区域)取决于实现。也可以用exit()它会刷新缓冲文件缓冲区,但不显示消息

解决方法 2: 一种比异常终止更灵活的方法是,使用函数返回值来指出问题。例如ostream类的get(void)函数通常返回下一个输入的字符的ASCII吗。但都到文件尾部时返回特殊的EOF(一般为signed char)。对haemonic()来说这种方式不管用,因为任何数值都是有效的返回值(答案)这时候我们就可以使用指针或者引用充当返回值。并使用函数返回值来指定返回的的成功和失败

#include <iostream>#include<cfloat>//for DBL_MAXusing std::cout;using std::cin;using std::endl;bool harmonic(double ,double ,double* );int main(int argc, char** argv) { double x,y,z; cout<<"enter two number"; while(cin>>x>>y) { if(harmonic(x,y,&z)) cout<<"harmonic mean of "<<x <<" and "<<y<<" is "<<z<<endl; else cout<<"one value should not be the negative" <<"of the ohter ,try again/n"; cout<<"enter next set of number<q to quit>:"; } cout<<"Bye!/n"; return 0;}bool harmonic(double a,double b,double* ans){ if(a==-b){ *ans=DBL_MAX; return false; } else{ * ans=2.0*a*b/(a+b); return true; }}

解决方法 3

#include <iostream>#include<cfloat>//for DBL_MAXusing std::cout;using std::cin;using std::endl;double harmonic(double ,double );int main(int argc, char** argv) { double x,y,z; cout<<"enter two number"; while(cin>>x>>y) { try{ z=harmonic(x,y); }catch(const char * s){ cout<<s<<endl; cout<<"Enter a new pair of number"; continue; } cout<<"harmonic mean of "<<x <<" and "<<y<<" is "<<z<<endl; cout<<"enter next set of number<q to quit>:"; } cout<<"Bye!/n"; return 0;}double harmonic(double a,double b){ if(a==-b){ throw "bad harmonic() arguments :a =-b not allowed"; } return 2.0*a*b/(a+b);}

异常的格式如下

try{ //抛出异常的语句}catch(exceptionType exceptionParameter){ //异常怎么处理写在这}

就像上面的示例那样throw在try中抛出了一个错误,错误类型是一串字符。try接受到错误,然后程序跳转到catch语句块。其中exceptionType 为我们throw抛出的参数类型。exceptionParameter是参数的名称,我们可以在catch语句块中使用这个参数。

将对象用作异常类型

#include <iostream>#include<cmath>//for sqrt()using std::cout;using std::cin;using std::endl;class bad_hmean{ private: double v1; double v2; public : bad_hmean(double a=0,double b=0):v1(a),v2(b){}; void mesg();};inline void bad_hmean::mesg(){ cout<<"hmean("<<v1<<","<<v2<<")" <<"incalid argument: a=-b"<<endl;}class bad_gmean{ public: double v1; double v2; bad_gmean(double a=0,double b=0):v1(a),v2(b){} const char* mesg();};inline const char* bad_gmean::mesg(){ return "gmean() arguments should be>=0/n";}double hmean(double a,double b);double gmean(double a,double b);int main(int argc, char** argv) { double x,y,z; cout<<"enter two number"; while(cin>>x>>y) { try{ z=hmean(x,y); cout<<"Harmonic mean of "<<x<<"and"<<y <<"is"<<z<<endl; cout<<"Geometric mean of "<<x<<"and"<<y <<"is"<<gmean(x,y)<<endl; }catch(bad_hmean & bg){ bg.mesg(); cout<<"try again"; continue; }catch(bad_gmean& hg){ cout<<hg.mesg(); cout<<"Value used:"<<hg.v1<<","<<hg.v2<<endl; cout<<"Sorry ,you don't get to play any more./n"; break; } cout<<"Bye!/n"; return 0; }}double hmean(double a,double b){ if(a==-b){ throw bad_hmean(a,b); } return 2.0*a*b/(a+b);}double gmean(double a,double b){ if(a<0||b<0){ throw bad_gmean(a,b)块块块块 } return std::sqrt(a*b);}

上面代码使用了对象作为throw 的返回类型,且使用多个catch语句块

异常规范和c++11

异常规范是告诉编译器一个函数师是否可能会产生异常,格式如下

int fun1()thow(bad_thing);//可能产生异常int fun2()thow();//不会产生异常

上述格式可以出现在函数声明定义上,这是c++98引入的概念,但是c++11已经摒弃这种做法了。因为其一因为会出现函数之间的调用回家打编译器的检查难度。其次如果我们更新了代码库,而以前的代码如果不修改可能会无法使用。 所以建议不要使用上述异常规范 c++11引用了noexcept来指出函数不产生异常 int main() noexcept 告诉编译器这个函数不会出现异常

还有运算符 noexcept(OperandName ) 用来判断操作数是否会引发异常

异常的重要特性

引发异常时编译器总会创建一个临时拷贝,即使异常规范和catch中指定时引用,我们会这样呢。请款下面例子:

class problem{};……void super()(proble){…… if(oh_no) { prolem oops; throw oops; }……}……try{ super();}catch(problem & p){//……}

上面的oops在函数执行完就没了,所以必须拷贝一个临时变量。 既然会创建临时变量为什么要用引用呢。毕竟引用作为返回值就是节省创建副本的开销啊。这是因为因为基类的引用可以使用派生类的方法。当有一系列的异常层次结构时。这是后基类的异常类型引用将匹配所有派生类的异常。这就需要注意一点就是catch的顺序了

class bad_1{};class bad_2:public bad_1{};class bad_3:public bad_2{};……try{}catch(bad_3){}catch(bad_2){}catch(bad_1){}

上面的catch语句开顺序是呵护清理的,因为bad_1是基类如果放在第一个catch中的话,抛出的所有异常都会有bad_1语句块处理,根本没有后面的catch{}什么事。

当我们调用一个函数时,我们可能不知道这个函数会抛出什么异常。但是我们也可以捕获异常。就是使用…

try{ fun()}catch(...){ cout<<"have exception";}

.


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表