异常:程序执行期间,可检测到的不正常情况。
例如:0作除数;数组下标越界;打开不存在的文件;远程机器连接超时;malloc失败等等。
程序的两种状态:
正常状态和异常状态,发生不正常情况后,进入异常状态,从当前函数开始,按调用链的相反次序,查找处理该异
常的程序片断。
1.throw 表达式
语义:用表达式的值生成一个对象(异常对象),程序进入异常状态。
Terminate函数,终止程序的执行。
2.try-catch语句
try{
包含可能抛出异常的语句;
}catch(类型名 [形参名]){
}catch(类型名 [形参名]){
}
例子程序:
#include <iostream>
#include <math.h>
using namespace std;
double sqrt_delta(double d){
if(d < 0)
throw 1;
return sqrt(d);
}
double delta(double a, double b, double c){
double d = b * b - 4 * a * c;
return sqrt_delta(d);
}
void main()
{
double a, b, c;
cout << "please input a, b, c" << endl;
cin >> a >> b >> c;
while(true){
try{
double d = delta(a, b, c);
cout << "x1: " << (d - b) / (2 * a);
cout << endl;
cout << "x2: " << -(b + d) / (2 * a);
cout << endl;
break;
}catch(int){
cout << "delta < 0, please reenter a, b, c.";
cin >> a >> b >> c;
}
}
}
3.重新抛出异常
语法: throw;
语义:重新抛出原有的异常对象。如果在throw后面有表达式,则抛出新的异常对象。
例子程序:
#include <iostream>
using namespace std;
void fun(int x){
try{
if(x == 1)
throw 1;
if(x == 2)
throw 1.0;
if(x == 3)
throw ''1'';
}catch(int){
cout << "catch an int in fun()" << endl;
}catch(double){
cout << "catch an double in fun()" << endl;
}
cout << "testing exception in fun()..."<< endl;
}
void gun()
{
try{
//fun(1);
//fun(2);
//fun(3);
fun(4);
}catch(char){
cout << "catch a char in gun()" << endl;
}
cout << "testing exception in gun()..."<< endl;
}
int main()
{
gun();
}
4.扑获所有异常
catch(...){
}
下面的程序是不对的:
error C2311: ''int'' : is caught by ''...'' on line 7
#include <iostream>
using namespace std;
void fun()
{
try{
}catch(...){
cout << "catch all exception ..." << endl;
}catch(int){
cout << "catch int exception ..." << endl;
}
}
5.异常规范
指出函数可以抛出的所有异常类型名。
语法:值类型函数名(形参表) throw(类型名表) 函数体
空异常规范表示不抛出异常;
例如:
warning C4297: ''function'' : function assumed not to throw an exception but does
__declspec(nothrow) or throw() was specified on the function
#include <io
stream>
using namespace std;
void function(int x) throw()
{
if(x == 1)
throw 1;
}
无异常规范表示可抛出任何异常。
异常规范违例,在函数的声明中并没有声明抛出该类异常,但在程序中却抛出了该类的异常?例如:
warning C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow)
void function(int x) throw (int)
{
if(x == 1)
throw 1.5;
}
注:在g++中并未警告。
对于函数指针,例如:
#include <iostream>
using namespace std;
void function(int x)throw(int)
{
if(x == 1)
throw 1;
}
int main()
{
void (*fp)(int)throw(char);
fp = function;
fp(1);
}
同样的,在g++中没有警告,但在vc8中提出警告:
warning C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow)
pan>
<补充>
异常规范违例,例子程序如下:
#include <iostream>
using namespace std;
class A
{
};
void function(int x)throw(int)
//void function(int x)throw(A*)
{
if(x == 1)
throw new A;
}
void test() throw (A* )
{
//void (*fp)(int)throw(A);
void (*fp)(int)throw(int);
fp = function;
try{
fp(1);
}catch(int)
{
cout << "test" << endl;
throw;
}
}
int main()
{
try{
test();
}catch(A*){
cout << "test in main" <<endl;
}
return 0;
}
这个代码在vc8和g++环境中的运行结果不同?
<补充>
1.异常处理仅仅通过类型而不是通过值来匹配的,否则又回到了传统的错误处理技术上去了,所以catch块的参数可以没有参数名称,只需要参数类型,除非要使用那个参数。
2.虽然异常对象看上去像局部对象,但并非创建在函数栈上,而是创建在专用的异常栈上,因此它才可以跨接多个函数而传递到上层,否则在栈清空的过程中就会被销毁。
3.函数原型中的异常说明要与实现中的异常说明一致,否则容易引起异常冲突。由于异常处理机制是在运行时有异常时才发挥作用的,因此如果函数的实现中抛出了没有在其异常说明列表中列出的异常,则编译器并不能检查出来。但是当运行时如果真的抛出了这样的异常,就会导致异常冲突。因为你没有提示函数的调用者:该函数会抛出一种没有被说明的即不期望的异常,于是异常处理机制就会检测到这个冲突并调用标准库函数unexcepted(),unexcepted()的默认行为就是调用terminate()来结束程序。
实际工作中使用set_unexcepter()来预设一个回调函数。
4.当异常抛出时局部对象如何释放?
Bjarne Stroustrup引入了“resource acquistion is initialization”思想,异常处理机制保证:所有从try到throw语句之间构造起来的局部对象的析构函数将被自动调用,然后清退堆栈(就像函数正常退出一样)。如果一直上溯到main函数后还没有找到匹配的catch块,那么系统调用terminate()终止整个程序,这种情况下不能保证所有局部对象会被正确地销毁。
5.catch块的参数应采用引用传递而不是值传递,不仅可以提高效率,还可以利用对象的多态性。另外,派生类的异常扑获要放到父类异常扑获的前面,否则,派生类的异常无法被扑获。catch(void *)要放到catch(...)前面。
6.编写异常说明时,要确保派生类成员函数的异常说明和基类成员函数的异常说明一致,即派生类改写的虚函数的异常说明至少要和对应的基类虚函数的异常说明相同,甚至更加严格,更特殊。