C与C++中的异常处理
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
C与C++中的异常处理(1)
1.异常和标准C对它的支持
(前言略)
1.1异常分类
基于Dr. GUI的建议,我把我的第一个专栏投入到“程序异常”的系列上。我认识到,“exception”这个术语有些不明确并和上下文相关,尤其是C++标准异常(C++ standard exceptions)和Microsoft的结构化异常(structured exception handling)。不幸的的是,“异常”一词太常见了,随时出现在语言的标准和常见的编程文献中。因为不想创造一个新名词,所以我将尽力在此系列的各部分中明确我对“异常”的用法。
l Part 1概述通常意义上的异常的性质,和标准C库提供的处理它们的方法。
l Part 2纵览Microsoft对这些标准C库方法的扩展:专门的宏和结构化异常处理。
l Part 3及其余将致力于标准C++异常处理体系。
(C语言使用者可能在Part2后放弃,但我鼓励你坚持到底;我所提出的许多点子同样适用于C,虽然不是很直接。)
本质上看,程序异常是指出现了一些很少发生的或出乎意料的状态,通常显示了一个程序错误或要求一个必须提供的回应。不能满足这个回应经常造成程序功能削弱或死亡,有时导致整个系统和它一起down掉。不幸的是,试图使用传统的防护方法来编制健壮的代码经常只是将一个问题(意外崩溃)换成了另外一个问题(更混乱的设计和代码)。
太多的程序员认为这个交换抵不上程序意外崩溃时造成的烦恼,于是选择了生活在危险之中。认识到这一点后,C++标准增加了一个优雅并且基本上不可见的“异常体系”到语言中;就这样,这个方法产生了。如同我们在Part4的开始部分将要看到的,这个方法大部分情况下很成功,但在很微妙的情况下可能失败。
1.2异常的生命阶段
在这个系列里,我将展示C和C++处理异常体系运行于异常整个生命期的每一阶段时的不同之处:
l阶段1:一个软件错误发生。这个错误也许产生于一个被底层驱动或内核映射为软件错误的硬件响应事件(如被0除)。
l阶段2:错误的原因和性质被一个异常对象携带。这个对象的类型可以简单的整数值到繁杂的C++类对象。
l阶段3:你的程序必须检测这个异常对象:或者轮询它的存在,或者由其主动上报。
l阶段4:检测代码必须决定如何处理异常。典型的方法分成三类。
a忽略异常对象,并期望别人处理它。
b在这个对象上干些什么,并还允许别人再继续处理它。
c获得异常的全部所有权。
l阶段5:既然异常已经处理了,程序通常恢复并继续执行。恢复分成两种:
a恢复异常,从异常发生处继续执行。
b终止异常,从异常被处理处继续执行。
当在程序外面(由运行期库或操作系统)终止异常时,恢复经常是不可能的,程序将异常结束。
我故意忽略了硬件错误事件,因为它们完全是底层平台范围内的事。取而代之,我假定一些软件上的可检测错误已经发生,并产生了一个处于第一阶段的软件异常对象。
1.3C标准库异常处理体系
C标准库提供了几个方法来处理异常。它们也全部在标准C++中有效,只是相关的头文件名字变了:老的C标准头文件
虽然基于向后兼容性,老的C头文件也被C++保留,但我建议你尽可能使用新的头文件。
对于绝大部分实际使用而言,最大的变化是在新的头文件中,申明的函数被包含在命名空间std 内。举个例子,C语言使用
#include
FILE *f = fopen("blarney.txt", "r");
在C++中被改成
#include
std::FILE *f = std::fopen("blarney.txt", "r");
或更C风格的
#include
using namespace std;
FILE *f = fopen("blarney.txt", "r");
不幸的是,Microsoft的Visual C++没有将这些新的头文件包含在命名空间std中,虽然这是C++标准所要求的(subclause D.5)。除非Visual C++在这些头文件中已经正确地支持了std,我将一直在我的专栏中使用老式的C风格命名。
(象MIcrosoft这样的运行库卖主这么做是合理的,正确地实现这些C程序库的头文件极可能要求维护和测试两份完全不同的底层代码,这是不可能受欢迎的也不值得多花力气的工作。)
1.4无条件终止
仅次于彻底忽略一个异常,大概最容易的异常处理方法是程序自我毁灭。有时,最懒的方法事实上是最正确的。
在你开始嘲笑以前,应该认识到,一些异常表示的状况是如此严重以致于怎么也不可能合理恢复的。也许最好的例子就是malloc时返回NULL。如果空闲堆管理程序不能提供可用的连续空间,你程序的健壮性将严重受损,并且恢复的可能性是渺茫的。
C库头文件
虽然两个函数在概念上是相联系的,但它们的效果不同:
l abort():程序异常结束。默认情况下,调用abort()导致运行期诊断和程序自毁。它可能会也可能不会刷新缓冲区、关闭被打开的文件及删除临时文件,这依赖于你的编译器的具体实现。l exit():文明地结束程序。除了关闭文件和给运行环境返回一个状态码外,exit()还调用了你挂接的atexit()处理程序。