【问题标题】:"We do not use C++ exceptions" — What's the alternative? Let it crash?“我们不使用 C++ 异常”——还有什么选择?让它崩溃?
【发布时间】:2010-08-16 01:45:05
【问题描述】:

"We do not use C++ exceptions."

如果不使用异常,出现错误时会发生什么?你就任由程序崩溃?

【问题讨论】:

  • 请不要采用 Google 代码样式指南,尽管世界上一切都是美好的。对于现代 C++ 项目来说,这很糟糕。 Just ignore it.
  • 有一种情况我可以想到异常不是一种选择(除了可能不支持它们的嵌入式 C++) - 当通过抽象接口在共享库中公开 C++ 类时。在这种情况下,无论是您自己的自定义 DLL(或 .so),还是 COM 接口或 XPCOM 接口,仅举几例,您都不能出现跨越接口边界的异常。因此,您必须求助于捕获所有异常并返回错误代码。否则我同意 GMan,不使用异常的麻烦是不值得的。
  • C 没有异常,它仍然可以让你处理异常情况。你可以在 C++ 中做同样的事情。
  • 你会像处理其他语言一样处理错误,没有例外:大量显式代码来检查每个函数调用的结果,以及当有人出错时进行长时间的调试会话。但是,除非像 Google 一样,你有大量不安全的遗留代码,否则你会疯狂地编写这样的代码。
  • 啊,这就是为什么 Windows 上的 AddressSanitizer 和 Dr Memory(由 Google 员工开发)不支持 C++ 中的异常处理:他们自己的项目根本不需要它...

标签: c++ exception


【解决方案1】:

或者您可以进一步阅读:

在他们看来,使用的好处 例外大于成本, 尤其是在新项目中。然而, 对于现有代码,引入 例外对所有人都有影响 依赖代码。如果可以例外 传播到一个新项目之外,它 整合也变得有问题 将新项目纳入现有项目 无异常代码。因为大多数 Google 现有的 C++ 代码不是 准备处理异常,它 比较难采用 生成异常的新代码。

鉴于 Google 现有的代码是 不是异常容忍的,成本 使用异常稍微大一些 比新项目的成本。 转换过程会很慢 并且容易出错。我们不相信 可用的替代方案 异常,例如错误代码和 断言,引入一个显着的 负担。

我们反对使用异常的建议是 不基于哲学或 道德依据,但实际依据。 因为我们想用我们的 Google 的开源项目和 如果那些 项目使用异常,我们需要 建议不要在 Google 中出现例外情况 开源项目也是如此。事物 如果我们有可能会有所不同 从头开始重新做一遍。

此规则有一个例外(没有 双关语)适用于 Windows 代码。

【讨论】:

  • 为什么?我已经阅读了整个 c++ 风格指南,并且我完全同意其中的大部分评论
  • @Karel:因为我们中的一些人没有将 C++ 添加到庞大的现有 C 代码库中,因此应该使用异常来报告错误。 Google 样式指南在样式和格式等方面都很好,但是“我们只使用 C++ 功能的一个子集”对于新代码来说根本不是好主意,除非该代码是用于不被其他人使用的库-新代码。
【解决方案2】:

不,另一种选择是做人们在 C 语言中所做的事情......你返回一个错误状态代码,指示函数是否成功,并且根据它可能失败的方式,你可能有一个或多个输出参数,您可以在其中指示失败的方式(或者您将失败的类型合并到错误状态代码中,这也是逐案处理的事情)。

【讨论】:

    【解决方案3】:

    返回码以外的异常替代方案:

    • LISP 样式的条件处理程序。
    • 软件信号和插槽,QT 风格。
    • 硬件中断或信号处理程序没有堆栈展开。嵌入式设备上的异常问题是堆栈展开。
    • longjmp / setjmp
    • 回调函数
    • 标志值,例如errorno (Unix)、GetLastError (Windows)、glGetError (OpenGL) 等
    • 消息队列,如果程序是事件驱动的
    • 可变函子。处理程序对象作为输入输出参数,指针在错误时改变。
    • 具有错误解决协程的光纤或线程
    • asm __int 3 或 CPU 等效项
    • 叠加说明或蹦床功能。

    ...还有更多。上面列出的许多都是嵌入式设备友好的。

    【讨论】:

    • longjmp / setjmp 听起来是个好主意! :D
    【解决方案4】:

    如果您根据定义不使用异常,则任何代码都不会抛出异常,因此不需要捕获它。

    这是“我们不使用 C++ 异常”,而不是“我们不捕获 C++ 异常”。

    【讨论】:

    • 啊我们没有错误,所以我们不必处理它们?您不需要异常来使程序崩溃,在忽略错误后写入无效的内存位置就可以了。所以让你的程序崩溃是异常的替代方法之一。
    • @josefx:在标准 C++ 中,写入无效内存位置是未定义的行为。 C++ 标准并没有规定它必须抛出异常,实际上在 linux 上它没有(它发送一个信号)。实际上,我认为发生这种情况时不会在任何操作系统上引发异常。我相信 Windows 会选择性地抛出一个结构异常(这与 C++ 异常不同),但仅此而已。
    • 我的意思是程序在忽略错误后已经处于无效状态并且信号很可能会杀死它=>您让它崩溃。不使用异常会删除 c++ 的内置错误处理,未处理的错误会损坏数据并导致程序崩溃。
    • @josefx:在错误后继续不会可靠地使程序崩溃;它可能同样容易损坏更多数据并导致更严重的故障。您不能依赖未定义行为的行为,如果程序处于无效状态,则唯一有效的选项是修复它或终止。
    • @Mike Seymour 我试图参考问题“出现错误时会发生什么?你只是让程序崩溃?”和 Bininis 回答说您不必捕获异常。正如捕获异常与程序崩溃无关 - 即使没有异常处理,它也可能崩溃。我倾向于以奇怪的方式思考,可悲的是它在我的一些 cmets 中表现出来:-(。
    【解决方案5】:

    链接的样式指南解释得很好:

    在他们看来,使用的好处 例外大于成本, 尤其是在新项目中。然而, 对于现有代码,引入 例外对所有人都有影响 依赖代码。如果可以例外 传播到一个新项目之外,它 整合也变得有问题 将新项目纳入现有项目 无异常代码。因为大多数 Google 现有的 C++ 代码不是 准备处理异常,它 比较难采用 生成异常的新代码。

    在 C++ 中创建健壮的代码相对容易,无需使用异常或担心异常保证。使用返回码和断言,异常实际上仅限于程序员错误。

    【讨论】:

      【解决方案6】:

      如果您在编写代码时发现了一个通常会引发异常的问题,但又希望遵守一些不使用异常的规定,那么您必须找到另一个让客户端代码知道错误的方法。

      正如许多现有答案文档一样,您可以返回一个标记值(例如,一个真/假成功值、一个枚举)。这种做法在 POSIX 和 libc 指定的常见 C 函数中非常熟悉,例如 fopen()、strstr() 或 printf()。

      另一个重要的选项是设置一些他们可以稍后查询的内部状态。为什么你可能想要或需要做后者?因为某些函数,尤其是 C++ 构造函数和运算符,通常不会让您有机会返回错误代码。例如,在:

      X x1(某物),x2(随便); fn(x1 + x2);

      X::X(...) 不能返回任何东西。 X::operator+ 可能会被调用(假设+ 不会在转换运算符的结果上调用),但fn() 可能期待const X&(或X&& 使用C++11),而operator+ 需要返回一个X,这样它就可以在成功的情况下工作。您没有机会返回不同类型的错误代码。 class X 可能需要设置一些内部状态,以供其他代码(可能是 fn(),可能是调用 fn() 之后的语句)测试以采取适当的措施。

      所以,你最终会得到这样的结果:

      X x1(something), x2(whatever);
      assert(x1.is_valid() and x2.is_valid());
      X x3 = x1 + x2;
      assert(x3.is_valid());
      fn(x3);
      

      请注意,此错误处理约定很冗长,容易被客户端编码人员忽略或忽略 - 这是创建异常的原因之一。大多数浮点硬件都使用了一个有趣的变化 - 某些操作(例如除以 0 或下/溢出)可能会将寄存器设置为标记值,​​例如非数字“NaN”或 +/- Infinity,然后是涉及参数的操作在这种状态下,将状态传播到它们的结果。例如,x = 8 + y / 0; z = x + 2; 也会将z 设置为哨兵。这里的想法是,您可以编写尽可能计算正确结果的代码,并在使用结果之前检查一次,以查看计算代码中任何地方的错误是否使该结果无效。有时它适用于数学代码,特别是当您没有根据变量的当前值做出分支决策时,但不幸的是,在许多情况下,您要么无法或不想让所有用户都使用以超级防御方式处理和传播错误状态的潜在无效目标代码。

      毫无例外地使用 C++ 严重损害了语言的可用性、可维护性、简洁性和优雅性。

      作为完全禁止使用异常的替代方案,在某些环境中,您可能能够在 API 边界捕获所有异常,然后以“C”样式返回错误代码或标记值。这允许更好的内部编码,但更好的外部互操作性。可悲的是,有时使用任何异常都是不切实际的,因为您的代码将在不提供异常处理机制的环境中执行……可能在内核、驱动程序或嵌入式环境中使用精简的 C++ 样式编译器。这样的环境不是真正的 C++,因为它不符合标准。

      【讨论】:

        【解决方案7】:

        您使用返回错误码的函数版本,并根据返回值进行操作。

        【讨论】:

          猜你喜欢
          • 2014-11-29
          • 1970-01-01
          • 2011-08-08
          • 2013-08-24
          • 1970-01-01
          • 2010-11-07
          • 2014-11-12
          • 1970-01-01
          • 2011-05-22
          相关资源
          最近更新 更多