@异常处理使得程序员能够将错误处理代码从程序执行的“主流程”中分离出来,提高程序的清晰度。
@在c++中,整数除法中如果除以0,程序将过早终止,在浮点数除法中,除数为0在一些版本的C++中是被允许的,它的结果是正或负的无穷大,输出为INF或-INF。
@runtime_error类,是标准库exception类的派生类,是C++描述运行时错误所创建的标准基类。
@exception是描述所有异常而创建的标准基类。
@一个从runtime_error派生而来的典型异常类只需要定义一个构造函数,这个构造函数将带有错误信息的字符串传递给基类runtime_error的构造函数
@几乎所有的异常类都从拥有一个virtual函数what的exception类派生而来,该函数返回异常项的错误信息。
例子: 一个处理除数为0的异常的类
#include<stdexcept>
using namespace std;
class DivideByZeroException:public runtime_error
{
public:
DivideByZeroException():runtime_error("divid by zero")
{}
};
验证代码:
#include "DivideByZeroException.h"
#include <iostream>
using namespace std;
double quotient(int numerator,int demoninator)
{
if(demoninator==0)
throw DivideByZeroException();
return static_cast<double> (numerator)/demoninator;
}
int main()
{
int number1;
int number2;
double result;
cout<<"Enter two integers(end of file to end)";
while(cin>>number1>>number2)
{
try
{
result= quotient(number1,number2);
cout<<"the quotient is:"<<result<<endl;
}
catch(DivideByZeroException ÷ByZeroException)
{
cout<<"Exception occurred:"<<divideByZeroException.what();
}
}
}
@c++提供try语句块使之能够进行异常处理,一个try语句块由关键字try和后面跟着的一对花括号构成,花括号中定义了可能发生错误的语句块。
@异常是在catch中捕获和处理的,在每个try后应该立即跟着至少一个catch,catch后面跟着的圆括号包含异常参数,说明该catch所能处理的异常类型。
@当一个try块中异常发生时,那么将要执行的就是能够匹配这个异常的catch处理器。
@注意,一个catch处理器只有一个参数,多个参数是错误。
@不能在一个try语句后有两个catch处理器捕获相同的异常。
@如果异常发生在try语句块中,并且没有匹配的catch处理器,或者异常语句不再try语句块中,则拥有该语句的函数将立即终止,并且程序将尝试
定位调用函数中封装的try语句块,这个过程称为堆栈展开。
@异常处理是设计用来处理同步错误的,不处理异步事件,只写时间发生在语句正在执行的时候,常见的例子是:数组下标越界,运算溢出,除数为零,无效的函数参数和内存分配失败。
@执行在catch处理器外的空throw语句将导致函数terminate被调用,程序放弃异常处理,并立即结束。
@catch处理器可以通过"throw;"重新抛出异常。
重新抛出异常的例子:
#include<iostream>
#include<exception>
using namespace std;
void throwException()
{
try
{
cout<<"before exception";
throw exception();
}
catch(exception &)
{
cout<<"exception happened";
throw;
}
}
int main()
{
try
{
cout<<"before excute function";
throwException();
}
catch(exception &)
{
cout<<"exception handle again.";
}
}
@异常规格,首先最好不要使用异常规格,除非重写一个已有异常规格的基类函数。
@异常规格声明如下:
int someFunction(double value)
throw(ExceptionA, ExceptionB, ExceptionC)
{
}
由上面可以看出,异常规格由紧接着函数参数列表的关键字throw开始,throw之后包含了能够抛出的异常类型
如果函数抛出了这些类型之外的异常,那么异常处理机制将会调用unexpected来处理,throw()不含任何参数,表示不抛出异常,如果抛出,将调用unexception()
@堆栈展开,当异常被抛出但是没有在一个特定区域被捕获,该函数的调用堆栈将被展开,别试图在展开的外部try。。catch中捕获该异常。
这要看下面的代码来理解更容易
#include<iostream>
#include<stdexcept>
using namespace std;
void function3()throw(runtime_error)
{
cout<<"in function3"<<endl;
throw runtime_error("runtime error in function3“);
}
void function2()throw(runtime_error)
{
cout<<" function3 is called in func2"<<endl;
function3();
}
void function1()throw(runtime_error)
{
cout<<" function2 is called in func2"<<endl;
function2();
}
int main()
{
try
{
cout<<"function is called inside main"<<endl;
function1();
}
catch(runtime_error &error)
{
cout<<"Exception occurred:"<<error.what()<<endl;
cout<<"Exception handled in main"<<endl;
}
}
首先function1()被执行,调用function2,function2被执行调用function3,function3抛出异常,但是没有异常处理,所以堆栈展开,
返回到function2,function2中也没有异常处理,所以继续展开到function1,function1中也没有,返回到main,main函数中有,所以catch被执行。
@处理new失败
1,C++标准指出,对于到new操作错误时,应当抛出bad_alloc异常(在头文件<new>中),下面是这种例子
#include<iostream>
#include<new>
using namespace std;
int main()
{
double *ptr[50];
try
{
fot(int i=0;i<50;i++)
{
ptr[i]=new double[5000000];
cout<<"ptr["<<i<<"] ok";
}
}
catch(bad_alloc &memoryException)
{
cout<<"Exception occurred"<<memoryException.what();
}
}
注意:(首先不推荐)在旧版本的C++中,new失败时将返回0,c++标准规定符合标准的编译器可以继续使用在失败时返回0的new版本,
所以头文件<new>定义了nothrow对象(nothrow_t类型)用法如下
double ×ptr=new(nothrow)double[78967895];
上述语句使用了没有抛出bad_alloc的new版本分配内存。
2,处理new失败的另一种方法是使用函数set_new_handler(在头文件<new>中)
该函数的参数是一个没有参数没有返回值的函数指针。
当new失败时,该函数被调用。这种犯法需要提前注册,如果没有注册new失败时候将会抛出bad_alloc异常。
c++明确规定new处理器将要完成以下工作中的一个
1,通过释放其他动态内存来增加可用内存,并返回运算符new来尝试再次分配内存。
2,抛出bad_alloc异常
3,调用函数abort或exit(在cstdlib中)
下面是例子
#include<iostream>
#include<new>
#include<cstdlib>
void newHandler()
{
cout<<"called"<<endl;
abort();
}
int main()
{
double *ptr[50];
set_new_handler(newHandler);
fot(int i=0;i<50;i++)
{
ptr[i]=new double[5000000];
cout<<"ptr["<<i<<"] ok";
}
}