您的位置:首页 > 编程语言 > C语言/C++

C++编程思想(第2卷)之异常

2006-02-13 10:08 148 查看
之前,写过两篇关于异常的Blog,不过仅仅涉及到异常比较浅的层面,现在我开始阅读《C++编程思想》关于异常的章节,希望进入到异常比较深的层面中去。
在C中,使用了一些比较传统的错误处理机制。通常这样的方法有三种:
1. 使用返回值或者全局标志,C中提供了errno和perror()这样的函数。这种函数我没有使用过,但是我用过windows的出错机制。在windows中常常使用GetLastError()来获取错误值,这可以是系统的错误值,还可以用SetLastError()自行设置。
2. 使用信号来控制,这带来库调用时,之间定义的Signal事件有冲突的问题。
3. 使用setjmp()、longjmp()函数。这类似于使用了goto,没有什么事,其实最好还是不要用了。
异常处理不支持自动类型转换,所以即便是定义了自动类型转换的构造函数在这里也不会生效。Handler所能捕获的异常只能在对象本身或者是在引用或者指针的情况下,捕捉对象的派生类。如果抛出的是基类的对象,用派生类是无法catch的。
异常Catch的排列通常是有讲究的,一般越窄的Handler放在越上面,象Catch-All这样只能放在最下面了。Catch的对象最好使用引用来Catch对象,这样不会因为成为基类而造成对象切片的情况。
重新抛出异常用throw,不加任何参数就能继续抛出。继续抛出这一特性的用途在于如果异常不能在一层函数处理,就需要分层来处理。比如说到一些层,需要清理一些堆内存的应用。否则,栈清空后一些堆内存的指针清掉了,这些内存就泄漏了。在Unix中,abort()这个函数在异常为捕获时调用。这时候全局和静态对象的析构函数没有调用。
一般来说,原则上是不在析构函数中抛出异常的。否则,这将导致一个异常被捕获后,在Handler清栈时导致另一个异常的抛出。
在异常抛出时,清栈时只清构造函数调用完成的对象。所以在构造函数中抛出异常是一件需要特别小心的事情。为了实现在构造函数中抛出异常,有一些办法可以使用。例如,在构造函数中捕获异常,这样做的好处是有效处理堆内存。目前,需要注意的是析构函数不要有异常了,否则这儿的逻辑会异常紊乱。还有的办法是使用智能指针之类的方法,来把堆内存的指针包装起来。这样会在清栈时调用对象的析构函数来清空堆内存。
我曾想一次能不能抛出两个异常?这是一件比较可笑的事情。因为在异常抛出的时候,代码就已经中断了,这时候只可能在一点上中断,也就是说,不可能同时出现两个异常。VC没有实现异常规范,这是需要注意的地方。C++和JAVA不同。C++在运行期检测异常规范,但JAVA在编译期检查。强烈谴责微软的偷工减料!
模板技术不适合应用异常规范,因为模板实例化有无数种可能性,所以不能确定什么样的异常会被抛出。我曾经疑惑为什么会把Top()和Pop()分开。结合异常来看就比较容易理解了。因为Pop()有可能抛出异常,所以在返回值作为参数来调用构造函数的时候会出现问题。
异常不适合用于异步事件,异步事件是要求效率非常高的;不适合良性错误条件,如果当即可以解决的话,就不要非得抛出异常。和Java不同,C++异常不用作流控制,因为效率不高。不要轻易试图对旧有代码加入异常,因为往往不理解代码而适得其反。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: