在C ++中使用Exceptions的可能的错误处理策略是什么,它们的后果和含义是什么?

问题描述 投票:7回答:1

我希望您能帮助理解在C ++中使用/禁用异常的可能方法。

我的问题不是关于什么是最佳选择,而是关于什么是可能的选择以及这些选项意味着什么。

目前,我能想到的选择是:

  1. 使用-fno-exceptions进行编译并放弃大多数std容器(可能定义不抛出的内部容器,如SpiderMonkey Coding_Style中建议的)
  2. 只是避免抛出并捕获自己的代码,但仍然使用可能抛出异常的std容器。对于这样的事实感到高兴,在例外的情况下,程序可以在没有堆栈展开的情况下终止,并且甚至RAII处理的外部资源也可能被搁置。 (这似乎是根据this SO question的答案的谷歌C ++方法)
  3. 没有使用异常但是将所有std :: exception包含在try块中以确保堆栈被解除并且在程序终止之前释放对外部资源的RAII句柄,如this Cert C++ rule
  4. 如上所述,但也会抛出异常,最终导致程序终止。
  5. 还使用catched异常并从异常中恢复。

我想知道我对选项的理解是否正确,以及我可能错过了什么或理解错误。

我还想知道给予基本异常安全的限制是否适用于选项2-4(异常总是最终导致程序终止)或者异常安全要求是否/如何放宽/限制于特定情况(例如处理外部资源) ,文件)。

c++ exception-handling
1个回答
3
投票

更新

严格地说,当你想要禁用异常时,只有编译时没有异常支持才是真正的方法,所以选项1.因为当你想要禁用异常时你也不应该使用甚至处理它们。引发异常会在大多数实现中立即终止或发生严重错误,只有这样才能避免空间或性能开销等开销问题,即使它们有多小(见下文)。

但是,如果您只是想知道哪些关于异常使用的范例,那么您的选项几乎是正确的,在程序启动时您没有提到保持异常异常以及可能或甚至可能抛出的事情的一般想法。

一般来说,更常见的是错误处理:例外是处理运行时遇到的错误,如果正确完成错误,您只能在运行时检测到错误。您可以通过简单地确保解决在运行之前可以检测到的所有问题来避免异常,例如正确的编码和检查(代码时间),使用严格的类型和检查(模板,编译时间),并再次重新检查它们(静态分析器)。

更新2

如果你理解你关于关心异常安全的错误我会说:基本上起初它取决于你一般启用异常:如果它们被禁用你就不能也不应该关心异常安全,因为没有(或者如果它们尝试成立,无论如何你将终止/崩溃/硬错。如果您启用了异常但不在代码中使用它们,例如情况2,4和3,那么无论如何都没有问题要终止,因此缺少清理代码是不相关的(并且3中的内容仍然可以运行案件)。但是后来应该向所有人明确表示你不想使用它们,所以他们不会试图从异常中恢复。如果你在你的代码中使用它们,你应该关心异常安全的方式,当异常被抛出时,你也会在自己之后进行清理,然后是主处理程序或未来的代码更改,当你仍然终止或可以恢复。不清理,但仍然使用异常是没有意义的。然后你可以坚持选择1。

据我所知,这应该是详尽无遗的。有关更多信息,请参阅下文

建议

我推荐选项4.我告诉你原因:

例外是一种非常误解和误用的模式。它们被误用于程序流程,就像Java过多的语言一样。它们在嵌入式或安全性代码中经常被禁止,因为它们的性质很难分辨哪个处理程序会捕获它,如果有的话,以及需要多长时间,C ++ std基本上只是说“实现魔法”。

背景

然而,在我的推理中,对异常的仇恨基本上是一个很大的XY问题:当人们抱怨他们的恢复被破坏而且难以分辨时,那么通常的问题是,看不到你不能或者应该做很多关于大多数例外,这就是它们的用途。然而,诸如超时或关闭的tcp连接之类的东西几乎不正常,但很多人使用异常,当然这是错误的。但是,如果您的整个操作系统告诉您没有网络适配器或没有更多内存,您可以做什么?您可能想要的唯一一件事就是尝试在某处记录原因,这应该在主块的一次尝试/捕获中完成。

对于安全/实时程序也是如此:对于内存不足的例子,当发生这种情况时你无论如何都是****,那么策略是在不安全的初始化时间做这些事情,其中​​例外是否定的问题也是。

当使用带有抛出成员的容器时,它会有类似的情况,当你得到错误代码时,你能做什么?并不多,这就是为什么你要在代码时确保没有错误的原因,比如确保元素确实在那里,或者为你的向量保留容量的原因。

这有清洁代码的好处,不要忘记检查错误和性能损失,至少使用像gcc等常见的C ++实现。

3.的原因是有争议的,你也可以在我看来这样做,但后来我的问题是:在析构函数中你需要做什么才能清理你的操作系统呢?操作系统会清理内存,套接字等。如果有一个独立的场景,问题仍然存在,只要不同:你计划停止,因为你的UART打破了例如,你想在停止之前做什么?为什么你不能在重新抛出之前在catch块中做到这一点?

总结一下

  1. 有没有使用任何抛出代码的问题,仍然留下如何处理罕见的错误代码的问题。 (这就是为什么这么多C程序员仍然使用goto或long jumps)
  2. 不可行恕我直言,两者都是最差的
  3. 如上所述,但你在静态DT中需要做什么,你甚至不正常终止不会做什么?
  4. 我的最爱
  5. 只有当你真的有罕见的条件,你实际上能够恢复

我的意思是作为一般建议:提升和异常应该意味着发生的事情在正常操作中永远不会发生,但是由于不可预见的故障而发生,例如硬件故障,分离的电缆,未找到的共享库,您已经完成的编程故障就像在容器中尝试.at()一样,你可能已经知道了,但容器却不能。然后,几乎每次都会导致程序终止/硬故障,抛出异常是合乎逻辑的

作为对异常支持进行编译的结果,例如对于独立的ARM程序,您的程序大小将增加27kB,可能没有托管(Linux / Windows等),以及2kB RAM用于独立式案例(请参阅C++ exception handler on gnu arm cortex m4 with freertos

但是,当您使用常用编译器(如clang或gcc)时,如果您的代码运行正常,即当您的分支/ ifs会抛出异常时,您不会因使用异常而导致性能损失。

作为参考,即我的陈述的证据,我参考ISO/IEC TR 18015:2006 Technical Report on C++ Performance,这个摘录来自7.2.2.3:

仅在实际使用异常时才为实时关键程序启用异常处理。完整的分析必须始终包括抛出异常,并且此分析始终取决于实现。另一方面,在异常情况下(例如,当连接发生故障时不需要处理来自设备的任何其他输入),在确定时间内动作的要求可能会松动。 §5.4给出了异常处理备选方案的概述。但是如图所示,所有选项都有其运行时成本,抛出异常可能仍然是处理异常情况的最佳方法。只要没有抛出任何异常(即throw-expression和handler之间只有几个嵌套函数调用),它甚至可能会降低运行时成本。

© www.soinside.com 2019 - 2024. All rights reserved.