C++异常(exception)第一篇--综合讲解

摘要:catch(exception &ex)是捕获所有标准库定义中的类std:exception;catch(...)则是捕获所有的异常。



1.简介

   异常是由语言提供的运行时刻错误处理的一种方式。提到错误处理,即使不提到异常,你大概也已经有了丰富的经验,但是为了可以清楚的看到异常的好处,我们还是不妨来回顾一下常用的以及不常用的错误处理方式。

 

C++异常之网络知识

1.1常用的错误处理方式

返回值。我们常用函数的返回值来标志成功或者失败,甚至是失败的原因。但是这种做法最大的问题是如果调用者不主动检查返回值也是可以被编译器接受的,你也奈何不了他:)这在C++中还导致另外一个问题,就是重载函数不能只有不同的返回值,而有相同的参数表,因为如果调用者不检查返回值,则编译器会不知道应该调用哪个重载函数。当然这个问题与本文无关,我们暂且放下。只要谨记返回值可能被忽略的情况即可。

 

全局状态标志。例如系统调用使用的errno。返回值不同的是,全局状态标志可以让函数的接口(返回值、参数表)被充分利用。函数在退出前应该设置这个全局变量的值为成功或者失败(包括原因),而与返回值一样,它隐含的要求调用者要在调用后检查这个标志,这种约束实在是同样软弱。全局变量还导致了另外一个问题,就是多线程不安全:如果多个线程同时为一个全局变量赋值,则调用者在检查这个标志的时候一定会非常迷惑。如果希望线程安全,可以参照errno的解决办法,它是线程安全的。

 

1.2不常用的处理方式

setjmp()/longjmp()。可以认为它们是远程的goto语句。根据我的经验,它们好象确实不常被用到,也许是多少破坏了结构化编程风格的原因吧。在C++中,应该是更加的不要用它们,因为致命的弱点是longjmp()虽然会unwindingstack(这个词后面再说),但是不会调用栈中对象的析构函数--够致命吧。对于不同的编译器,可能可以通过加某个编译开关来解决这个问题,但太不通用了,会导致程序很难移植。

 

1.3异常

现在我们再来看看异常能解决什么问题。对于返回值和errno遇到的尴尬,对异常来说基本上不存在,如果你不捕获(catch)程序中抛出的异常,默认行为是导致abort()被调用,程序被终止(coredump)。因此你的函数如果抛出了异常,这个函数的调用者或者调用者的调用者,也就是在当前的callstack上,一定要有一个地方捕获这个异常。而对于setjmp()/longjmp()带来的栈上对象不被析构的问题对异常来说也是不存在的。那么它是否破坏了结构化(对于OOparadigms,也许应该说是破坏了流程?)呢?显然不是,有了异常之后你可以放心的只书写正确的逻辑,而将所有的错误处理归结到一个地方,这不是更好么?

 

综上所述,在C++中大概异常可以全面替代其它的错误处理方式了,可是如果代码中到处充斥着try/throw/catch也不是件好事,欲知异常的使用技巧,请保持耐心继续阅读:)


2.异常的语法

在这里我们只讨论一些语法相关的问题。

 

2.1try

try总是与catch一同出现,伴随一个try语句,至少应该有一个catch()语句。try随后的block是可能抛出异常的地方。

 

2.2catch

catch带有一个参数,参数类型以及参数名字都由程序指定,名字可以忽略,如果在catch随后的block中并不打算引用这个异常对象的话。参数类型可以是build-intype,例如int,long, char等,也可以是一个对象,一个对象指针或者引用。如果希望捕获任意类型的异常,可以使用“...”作为catch的参数。

 

catch不一定要全部捕获tryblock中抛出的异常,剩下没有捕获的可以交给上一级函数处理。

 

2.3throw

throw后面带一个类型的实例,它和catch的关系就象是函数调用,catch指定形参,throw给出实参。编译器按照catch出现的顺序以及catch指定的参数类型确定一个异常应该由哪个catch来处理。

throw不一定非要出现在try随后的block中,它可以出现在任何需要的地方,只要最终有catch可以捕获它即可。即使在catch随后的block中,仍然可以继续throw。这时候有两种情况,一是throw一个新类型的异常,这与普通的throw一样。二是要rethrow当前这个异常,在这种情况下,thro

你可能感兴趣的:(C++异常(exception)第一篇--综合讲解)