返回介绍

7.1 背景

发布于 2024-08-19 12:44:37 字数 3231 浏览 0 评论 0 收藏 0

C++ 从 C 语言中继承了各种基于错误返回码的机制,错误可以用特殊值、全局状态、局部状态和回调等多种方式表达。例如:

double sqrt(double d);  // 当 d 为负数时,设置 errno 为 33
int getchar();          // 遇到文件结尾返回 -1
char* malloc(int);      // 如果分配出错,返回 0

C++ 的早期用户(1980 年代)发现这些技术令人困惑,也不足以解决所有问题。返回 (值,错误码) 对变得流行,但这更增加了混乱和变化。例如:

Result r = make_window(arguments); // Result 是 (值,错误码) 对
if (r.error) {
    // ... 错误处理 ...
}
Shape* p = r.value;

繁琐的重复错误检查使代码变得混乱。使用错误码时,很难将程序的主要逻辑与错误处理区分开。程序的主线(业务逻辑)与大量奇怪和模糊的错误处理代码紧密耦合在一起。对于那些错误处理本身就是主要的复杂逻辑而言,这种基于错误返回码的处理方式可能会带来严重的问题。

使用包含 (值,错误码) 对的类会带来巨大的成本。除了检测错误码的成本外,许多 ABI(应用程序二进制接口)甚至不使用寄存器来传递小的结构体,所以 (值,错误码) 对不仅传递了更多的信息(是通常数量的两倍),而且也使传递的性能有数量级的降低。可悲的是,在许多 ABI 中,尤其那些针对嵌入式系统的 ABI(专为 C 代码设计),这个问题直到今天(2020 年)依然存在。

此外,并不存在真正的好方法可以使用错误码来处理发生在构造函数中的错误(构造函数没有返回值),还有那些过去流行的具有复杂类层次结构的系统,子对象创建中各种潜在错误也很难通过错误码的方式处理。

还有,对于所有传统的错误处理技术,最令人头疼的是人们会忘记检查错误。这一直是错误的主要根源,并且在 2020 年的今天依旧如此。C++ 异常机制的主要目标是使不完整或复杂的错误处理中的错误最小化。

C++ 异常是在 1988–89 年设计的,旨在解决当时普遍存在的复杂且容易出错的错误处理技术。它们记录在 ARM(The Annotated C++ Reference Manual)[Ellis and Stroustrup 1990] 中,并作为标准基础文档 [Stroustrup 1993] 的一部分被 ANSI C++ 所采用。

与其他语言的异常设计相比,用于 C++ 的异常设计由于 C++ 代码需要和其他语言(尤其是 C)的代码结合使用而变得复杂。考虑一个 C++ 函数 f() 调用一个 C 函数 g(),该函数又调用一个 C++ 函数 h()。现在 h() 抛出异常由 f() 捕获。通常,C++ 函数不知道被调用函数的实现语言。这样的场景使我们不能通过修改函数签名以添加异常传播参数,或隐式地向返回类型添加返回码的方法做错误处理。

与使用其他技术相比,异常与 RAII(§2.2) 一起解决了许多棘手的错误处理问题(例如,如何处理构造函数中的错误以及那些远离错误处理代码的错误),而且所需的时间成本要小得多(与 1990 年代中期所用的技术相比通常不到 3%,甚至更便宜)。虽然异常从来都不是没有争议的,但我还是低估了它们引起争议的可能性。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文