- 论坛徽章:
- 0
|
错误处理和异常处理,你用哪一个
2. 异常的语法
在这里我们只讨论一些语法相关的问题。
2.1 try
try总是与catch一同出现,伴随一个try语句,至少应该有一个catch()语句。try随后的block是可能抛出异常的地方。
2.2 catch
catch带有一个参数,参数类型以及参数名字都由程序指定,名字可以忽略,如果在catch随后的block中并不打算引用这个异常对象的话。参数类型可以是build-in type,例如int, long, char等,也可以是一个对象,一个对象指针或者引用。如果希望捕获任意类型的异常,可以使用“...”作为catch的参数。
catch不一定要全部捕获try block中抛出的异常,剩下没有捕获的可以交给上一级函数处理。
2.3 throw
throw后面带一个类型的实例,它和catch的关系就象是函数调用,catch指定形参,throw给出实参。编译器按照catch出现的顺序以及catch指定的参数类型确定一个异常应该由哪个catch来处理。
throw不一定非要出现在try随后的block中,它可以出现在任何需要的地方,只要最终有catch可以捕获它即可。即使在catch随后的block中,仍然可以继续throw。这时候有两种情况,一是throw一个新类型的异常,这与普通的throw一样。二是要rethrow当前这个异常,在这种情况下,throw不带参数即可表达。例如:
try{
...
}
catch(int){
throw MyException("hello exception" ; // 抛出一个新的异常
}
catch(float){
throw; // 重新抛出当前的浮点数异常
}
2.4 函数声明
还有一个地方与throw关键字有关,就是函数声明。例如:
void foo() throw (int); // 只能抛出int型异常
void bar() throw (); // 不抛出任何异常
void baz(); // 可以抛出任意类型的异常或者不抛出异常
如果一个函数的声明中带有throw限定符,则在函数体中也必须同样出现:
void foo() throw (int)
{
...
}
这里有一个问题,非常隐蔽,就是即使你象上面一样编写了foo()函数,指定它只能抛出int异常,而实际上它还是可能抛出其他类型的异常而不被编译器发现:
void foo() throw (int)
{
throw float; // 错误!异常类型错误!会被编译器指出
...
baz(); // 正确!baz()可能抛出非int异常而编译器又不能发现!
}
void baz()
{
throw float;
}
这种情况的直接后果就是如果baz()抛出了异常,而调用foo()的代码又严格遵守foo()的声明来编写,那么程序将abort()。这曾经让我很恼火,认为这种机制形同虚设,但是还是有些解决的办法,请参照“使用技巧”中相关的问题。 |
|