Gia*_*nni 7 c++ exception-handling
我希望您能帮助理解在C++中使用/禁用异常的可能方法.
我的问题不是关于什么是最佳选择,而是关于什么是可能的选择以及这些选项意味着什么.
目前,我能想到的选择是:
我想知道我对选项的理解是否正确,以及我可能错过了什么或理解错误.
我还想知道给予基本异常安全的限制是否适用于选项2-4(异常总是最终导致程序终止)或者异常安全要求是否/如何放宽/限制于特定情况(例如处理外部资源) ,文件).
严格来说,当您想要禁用异常时,仅在不支持异常的情况下进行编译才是正确的选择,因此选择 1。因为当您想要禁用异常时,您也不应该使用甚至处理它们。在大多数实现中,引发异常会立即终止或出现硬故障,只有这样,您才能避免空间或性能开销等开销影响,即使它们有多小(见下文)。
\n\n但是,如果您只是想知道存在哪些关于异常使用的范例,那么您的选择几乎是正确的,您没有提到保持异常异常以及在程序中执行可能甚至可能抛出的事情启动。
\n\n更一般地说,涉及到一般的错误处理:异常是用来处理运行时遇到的错误的,如果正确完成,错误只能在运行时检测到。您可以通过简单地确保解决运行前可以检测到的所有问题来避免异常,例如正确的编码和审查(代码时间)、使用严格的类型和检查(模板、编译时间)以及再次重新检查它们(静态分析器)。
\n\n如果您对关心异常安全的理解是错误的,我会说:\n基本上,首先这取决于您何时启用一般异常:如果禁用它们,您就不能也不应该关心异常安全,因为没有(或者如果它们试图存在,无论如何你都会终止/崩溃/硬故障)。\n如果你启用异常但不在你的代码中使用它们,就像情况 2、4 和 3 一样,那么你想终止也没问题无论如何,所以缺少清理代码是不相关的(并且 3. 中的内容在任何情况下仍然运行)。但随后应该让每个人都清楚你不想使用它们,这样他们就不会尝试从异常中恢复。\n如果你在代码中使用它们,你应该关心异常安全,当抛出异常时,你也会在你自己之后进行清理,然后它是主处理程序或未来的代码更改,当你仍然终止或可以恢复。不清理但仍然使用异常是没有意义的。然后你就可以坚持选择1。
\n\n据我所知,这应该是详尽的。欲了解更多信息,请参阅下文。
\n\n我推荐选项 4。我告诉你原因:
\n\n异常是一种非常容易被误解和误用的模式。它们被滥用于程序流,就像 Java 语言过度滥用一样。它们通常在嵌入式或安全代码中被禁止,因为它们的性质很难判断哪个处理程序会捕获它(如果有的话),以及这需要多长时间,C++ std 只是说基本上是“实现魔法”。
\n\n然而,在我的推理中,对例外的厌恶基本上是一个很大的 XY 问题:\n当人们抱怨他们的恢复被破坏且难以判断时,那么通常的问题是,他们看不到你不能或应该做关于大多数例外情况,这就是它们的用途。然而,诸如超时或关闭 tcp 连接之类的事情并不是不正常的,但许多人为此使用异常,这当然是错误的。但是,如果您的整个操作系统告诉您没有网络适配器或没有更多内存,您该怎么办?您可能想要的唯一一件事就是尝试在某处记录原因,这应该在主块周围的一次 try/catch 中完成。
\n\n对于安全/实时程序来说也是如此:对于内存不足的例子,当这种情况发生时,无论如何你都是****,那么策略就是在不安全的初始化时间做这些事情,其中没有异常问题也。
\n\n当使用带有抛出成员的容器时,这是一个类似的场景,当您收到错误代码时,您能做什么?不多,这就是为什么您要在代码时确保没有错误的原因,例如确保元素确实在那里,或者为向量保留容量。
\n\n这样做的好处是代码更干净,不会忘记检查错误,并且不会造成性能损失,至少使用常见的 C++ 实现(如 gcc 等)。
\n\n3. 的原因是有争议的,我认为你也可以这样做,但我的问题是:你必须在析构函数中做什么,无论如何都不会清理你的操作系统?操作系统会清理内存、套接字等。如果有一个独立的场景,无论何时不同,问题仍然存在:例如,您计划因为 UART 损坏而停止,在停止之前您想做什么?为什么不能在重新抛出之前在 catch 块中执行此操作?
\n\n存在不使用任何抛出代码的问题,并且仍然留下如何处理罕见错误代码的问题。(这就是为什么这么多 C 程序员仍然使用 goto 或长跳转)
恕我直言,不可行,两者中最糟糕的
如上所述,好的,但是您需要在静态 DTors 中做什么,甚至非正常终止也不会做什么?
我的最爱
仅当您确实患有罕见病症并且能够真正康复时
我的意思是作为一般建议:引发和异常应该意味着发生了正常操作中不应该发生的事情,但由于不可预见的故障而发生,例如硬件故障,分离的电缆,未找到共享库,您所做的编程错误,就像尝试在不在容器中的索引处执行 .at() ,您可能知道这一点,但容器却不能。那么抛出异常几乎每次都会导致程序终止/硬故障是合乎逻辑的
\n\n使用异常支持进行编译的结果是,例如,对于独立式 ARM 程序,您的程序大小将增加 27kB,对于托管程序(Linux/Windows 等)可能不会增加,并且对于独立式情况,程序大小将增加 2kB RAM(请参阅C++ 异常) gnu arm cortex m4 上的处理程序与 freertos)
\n\n然而,当使用常见的编译器(如 clang 或 gcc)时,当您的代码正常运行时,即当您的分支/if 不会触发会引发异常时,您不会因使用异常而付出任何性能损失。
\n\n作为我的陈述的参考,即证据,我参考了ISO/IEC TR 18015:2006 C++ 性能技术报告,其中摘录自 7.2.2.3:
\n\n\n\n仅当实际使用异常时才启用实时关键程序的异常处理。完整的分析必须始终包括异常的抛出,并且该分析始终依赖于实现。另一方面,在发生异常的情况下,在确定的时间内采取行动的要求可能会放松(例如,当连接中断时,无需再处理来自设备的任何输入)。\xc2\xa75.4 中给出了异常处理替代方案的概述。但如图所示,所有选项都有其\n 运行时成本,并且抛出异常可能仍然是\n 处理异常情况的最佳方法。只要没有长时间抛出异常(即,抛出表达式和处理程序之间只有几个嵌套函数调用),它甚至可能会降低运行时成本。
\n