D 中的错误处理 2.014

无版本差异


我来了,我编码了,我崩溃了。-- Julius C'ster
所有的程序都要应付错误。错误是不在程序正常操作范围内的异常情况:常见的错误情况
有:
• 内存耗尽。
• 磁盘空间耗尽。
• 文件名无效。
• 试图写只读文件。
• 试图读不存在的文件。
• 请求不支持的系统服务。
21.1 错误处理问题
C 语言检测报告错误的传统方法并没形成传统,每个函数都有自己的方法,包括:
• 返回 NULL 指针。
• 返回 0 值。
• 返回非零的错误代码。
• 需要检查 errno。
• 如果上述检查失败了,需要调用一个函数。
为了处理可能出现的错误,必须为每个函数添加繁琐的错误处理代码。如果发生了错误,必
须有错误恢复代码,并且需要有代码将错误以某种友好的方式告诉给用户。如果不能在发生
错误的局部环境处理错误,就必须显式地将错误传播给它的调用者。如果要显示错误类型,
需要将一大堆 errno 值转换为合适的文字。这些代码可能占用整个项目编码时间的一大部
分,而且如果运行时系统加入了一个新的 errno 值,旧有的代码就不能显示有意义的信息。
功能良好的错误处理代码很容易将清晰而简洁的实现搞得一团糟。
更糟的是,功能良好的错误处理代码本身就很容易出错,还总是整个项目中测试最不充分的
部分(因此充满错误),并且还总是被简单地省略。最终的结果就如同“蓝屏死机”那样,
程序由于无法处理一些未预料到的错误而失败。
并不值得为“快速而脏的(quick and dirty)”程序编写错误处理代码,因此使用这类程序就好
像使用没有锯罩的多功能台式电锯。
我们所需要的是如下的错误处理哲学和方法学:
• 标准化——如果用法一致,它就更有用。
• 就算程序员无法查出出现了什么错误,也要使程序以合适的方式终止。
• 在无需改动旧代码的前提下就可以加入新的错误类型,从而重用旧的代码。
• 不会由于粗心大意而忽略某些错误。
• 重写“快速而脏的”程序直到能够正确处理错误。
• 可以很容易地写出清晰的错误处理代码。
197
第 21 章 D 中的错误处理 — 张雪平
21.2 D 的错误处理解决方案
先让我们观察一下并对错误作出以下假设:
• 错误不属于正常的程序流。错误是异常的、不常见的、不该出现的。
• 因为错误不常见,所以错误处理代码的性能并不十分重要。
• 程序的正常逻辑流的性能很关键。
• 所有的错误必须采用统一的方式处理,不论是显式地编写代码处理它们还是采用系统
默认的处理方式。
• 相比于必须从错误里进行恢复的代码,负责检测错误的代码了解更多关于错误的信
息。
解决方案是使用异常处理来报告错误。所有的错误都是从抽象类 Error 派生的对象。Error
类有一个叫做 toString() 的纯虚函数,它返回一个 char[] 类型的人类可读的错误描述。
如果代码检测到了一个错误,如“内存耗尽”,则抛出 Error 对象,其中包含消息“内存耗
尽”。函数调用堆栈会被展开,并查找 Error 的处理程序。随着堆栈的展开,相应的
Finally 块 会被执行。如果找到了错误处理程序,就在该处恢复程序的执行。否则,会运行
默认的错误处理程序,该程序会显示错误消息并终止程序。
这个解决方案是否能满足我们的要求?
标准化——如果用法一致,它就更有用。
这正是 D 的方法,D 运行时库和示例都采用这种方式。
就算程序员无法查出出现了什么错误,也要使程序以合适的方式终止。
如果没有相应的错误处理程序,程序会执行默认的错误处理程序,该处理程序打印出
适当的消息,并使程序优雅的推出。
在无需改动旧代码的前提下就可以加入新的错误类型,从而重用旧的代码。
旧的代码可以决定是捕获所有的错误,或者是只捕获特定的错误并向上传播剩余的错
误。无论那种情况,都不必为每个错误代码关联一个消息了,编译器会自动为其提供
正确的消息。
不会由于粗心大意而忽略某些错误。
异常会采用这种或那种方式处理。不会出现使用 NULL 指针表示出错,但后面的代码
却试图使用该 NULL 指针的情况。
重写“快速而脏的”程序直到能够正确处理错误。
“快速而脏的”代码根本不需要包含任何错误处理代码,也不需要检查错误。错误会
被默认的处理程序捕获,显示适当的消息,然后程序会优雅的终止。
可以很容易地写出清晰的错误处理代码。
try/catch/finally 语句比那些无穷无尽的 if (出错) goto 处理程序; 语句要清晰得多。
这个解决方案是否符合我们对于的错误的假设?
错误不属于正常的程序流。错误是异常的、不常见的、不该出现的。
D 的异常处理机制完全符合上述假定。
因为错误不常见,所以错误处理代码的性能并不十分重要。
异常处理堆栈的展开相对较慢。
198
第 21 章 D 中的错误处理
程序的正常逻辑流的性能很关键。
因为正常的代码流不必检查每个函数调用的返回值是否代表出错,使用异常处理来处
理错误确实会使程序运行得更快。
所有的错误必须采用统一的方式处理,不论是显式地编写代码处理它们还是采用系统默认的
处理方式。
如果某个错误没有对应的处理程序,就会由运行时库中的默认处理程序处理。如果一
个错误被忽略了,那一定是程序员特意添加了代码用于忽略这个错误,因此这一定是
在程序员意料之中的。
相比于必须从错误里进行恢复的代码,负责检测错误的代码了解更多关于错误的信息。
不需要将错误代码翻译为人类可读的字符串,错误检测代码而不是错误恢复代码负责
生成正确的字符串。这种做法也使不同的程序出现相同的错误时会输出相同的消息。
使用异常来处理错误会导致其它的问题——如何写出异常安全的程序。这里有关于怎么做的
说明。

你可能感兴趣的:(D语言)