异常处理
- 常见的异常:除0溢出,数组下标越界,所要读取的文件不存在,空指针,内存不足等等。
- c++的异常一旦抛出,如果不捕获,则程序直接退出。
- c语言通过返回值判断,但有缺陷:1.容易忽略。2.容易和正常值混淆。
abort()函数
- 向标准错误流(cerr使用的错误流)发送消息:abnormal program termination(程序异常终止),然后终止程序。
- 也可以使用exit()。
异常机制
- 抛出异常:throw xxx;
- 将可能有异常的代码放在try{}中,然后用catch(抛出的异常类型){} 接收抛出的异常。
- 可以用catch(…){} 接收任何类型的异常。
- 例子:
int myDiv01(int a,int b)
{
if(b==0)
throw 0;//抛出异常
return a/b;
}
void test02( )
{
try{
int ret = myDiv01(10, -10) ;
cout<<"ret = "<<ret<<endl;
}
catch(int e)//只不过抛出的异常是int类型
{
cout<<"捕获到int类型异常e = "<<e<<end1 ;
}
}
- 抛出指定类型的异常:
//只能抛出int char 异常
void testFunc02() throw(int,char)
{
throw 3.14f ;
}
/ /函数不抛出任何异常
void testFunc03() throw()
{
throw 10;
}
返回错误码
栈解旋(unwinding)
- 异常被抛出后,从进入try块起,到异常被抛出前,这期间在栈上构造的所有对象都会被自动析构。
void test03( )
{
try{
Person ob1("00_德玛") ;
Person ob2("01_小炮") ;
Person ob3("02_小法");
Person ob4("03_提莫") ; .
throw 10;
}
catch(int)
{
cout<<"捕获到int异常"<<endl;
}
cout<<"其他工作"<<end1 ;
- ob1,ob2,ob3,ob4都被析构。
异常的生命周期
- 可以通过抛出匿名对象达到相同的效果(推荐):
void test05( )
{
try{
throw MyException() ;
}
catch (MyException &e)
{
cout<<"捕获到MyException &"<<endl ;
}
}
标准异常
- exception是所有异常的基类,往下的都是它的子类:
- 对于使用new导致的内存分配问题,C++的最新处理方式是让new引发bad_alloc异常。头文件new包含bad_alloc类的声明,它是从exception类公有派生而来的。但在以前,当无法分配请求的内存量时,new返回一个空指针。
- 空指针和new:很多代码都是在new在失败时返回空指针时编写的。为处理new的变化,有些编译器提供了一个标记(开关),让用户选择所需的行为。当前,C++标准提供了一种在失败时返回空指针的new,其用法如下:
int * pi = new (std::nothrow) int;
int * pa = new (std::nowthrow) int[500];
-
domain_error;
-
invalid_argument;
-
length_error;
-
out_of_bounds。
-
每个类独有一个类似于logic_error的构造函数,让您能够提供一个供方法what()返回的字符串。
-
数学函数有定义域(domain)和值域(range)。定义域由参数的可能取值组成,值域由函数可能的返回值组成。例如,正弦函数的定义域为负无穷大到正无穷大,因为任何实数都有正弦值;但正弦函数的值域为−1到+1,因为它们分别是最大和最小正弦值。另一方面,反正弦函数的定义域为−1到+1,值域为−π到+ π。如果您编写一个函数,该函数将一个参数传递给函数std::sin(),则可以让该函数在参数不在定义域−1到+1之间时引发domain_error异常。
-
异常invalid_argument指出给函数传递了一个意料外的值。例如,如果函数希望接受一个这样的字符串:其中每个字符要么是‘0’要么是‘1’,则当传递的字符串中包含其他字符时,该函数将引发invalid_argument异常。
-
异常length_error用于指出没有足够的空间来执行所需的操作。例如,string类的append()方法在合并得到的字符串长度超过最大允许长度时,将引发length_error异常。
-
异常out_of_bounds通常用于指示索引错误。例如,您可以定义一个类似于数组的类,其operator() [ ]在使用的索引无效时引发out_of_bounds异常。
-
每个类独有一个类似于runtime_error的构造函数,让您能够提供一个供方法what()返回的字符串。
-
下溢错误(underflow_error)在浮点数计算中。一般而言,存在浮点类型可以表示的最小非零值,计算结果比这个值还小时将导致下溢错误。
-
整型和浮点型都可能发生上溢错误(overflow_error),当计算结果超过了某种类型能够表示的最大数量级时,将发生上溢错误。
-
计算结果可能不再函数允许的范围之内,但没有发生上溢或下溢错误,在这种情况下,可以使用range_error异常。
-
例子:
class Person
{
private :
int age;
public :
Person(int age)
{
if(age<0|| age > 150)
throw out_of_range("age无效") ;
this->age =age;
}
};
void test06()
{
try{
Person ob(200) ;
}
catch(exception &e)
{
cout<<"捕获到异常"<<e. what()<<endl;
}
}
terminate函数
- 未捕获的异常不会立刻终止,程序首先调用terminate()函数,默认情况下,terminate()调用abort(),可以用set_terminate()指定terminate()要调用的函数,这两者都在头文件exception中声明。
void myQuit()
{
cout << "Terminating due to uncaught exception\n";
exit(5);
}
set_terminate(myQuit);
- 如果引发的异常没有被捕获(catch)时,程序调用terminate(),然后再调用myQuit()。
unexpected函数
- 发生意外异常,程序调用此函数,然后unexpected调用terminate,再默认调用abort(),也可以用set_unexpected()修改要调用的函数,此函数包含在头文件exception中。
- 总之,要捕获所有异常:
#include <exception>
using namespace std;
然后,设计一个替代函数,将意外异常转换为bad_exception异常,
该函数的原型如下:
void myUnexpected()
{
throw std::bad_exception(); //or just throw;
}
仅使用throw,而不指定异常将导致重新引发原来的异常。
然而,如果异常规范中包含了这种类型,
则该异常将被bad_exception对象所取代。
接下来在程序的开始位置,将意外异常操作指定为调用该函数:
set_unexpected(myUnexpected);
最后,将bad_exception类型包括在异常规范中,
并添加如下catch块序列:
double Argh(double, double) throw(out_of_bounds, bad_exception);
...
try {
x = Argh(a, b);
}
catch(out_of_bounds & ex)
{
...
}
catch(bad_exception & ex)
{
...
}