程序有时会遇到运行阶段的错误,比如打开一个不存在的文件,请求过多内存,接受一个不能使用值……通常程序员会避免这样的的以外,而c++提供了一种功能强大的而灵活的工具——异常。
例如下面这个示例
int main(int argc, char** argv) {
int x=19;
int y=-x;
std::cout<<(2*x*y/(x+y));/分母不能为0
return 0;
}
上面是输出两个数的调和平均数(两个数的倒数的平均数的倒数)
对于上面程序而言编译器可能输出一个表示无穷大的浮点数值,cout将这个值输出为Inf,inf,INF(Infinitely),也可能直接崩溃。这取决你的编译器,而我用的g++直接奔溃了
解决方法 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_MAX
using 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_MAX
using 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()(problem)
{
……
if(oh_no)
{
problemoops;
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";
}
#.#