你(真的)写出异常安全的代码吗?

liste 发布于 2019-11-10 c++ 最后更新 2019-11-10 12:10 265 浏览

异常处理(EH)似乎是当前的标准,并且通过搜索网络,我找不到任何尝试改进或取代它的新颖想法或方法(当然,存在一些变化,但没有新颖性)。 尽管大多数人似乎忽略了它,或者只是接受它,但EH有一些巨大的缺点:代码中看不到例外,它创造了许多可能的退出点。乔尔在软件上写了article about it。与goto的比较非常吻合,这让我再次想到了EH。 我尽量避免使用EH,只使用返回值,回调或任何符合目的的东西。但是当你必须编写可靠的代码时,你现在不能忽略EH:它从new开始,它可能会抛出一个异常,而不是仅仅返回0(就像过去一样)。这使得任何一行C++代码都容易受到异常的影响。然后,C++基础代码中的更多地方会抛出异常...... std lib会执行此操作,依此类推。 这感觉就像走在摇摇欲坠的理由上。所以,现在我们被迫关心异常! 但它很难,它真的很难。你必须学会​​写异常安全的代码,即使你有一些经验,仍然需要仔细检查任何一行代码才能安全!或者你开始在任何地方放置try / catch块,这会混淆代码,直到达到不可读状态。 EH取代了旧的清晰的确定性方法(返回值..),它只有一些但易于理解和易于解决的缺点,并在代码中创建了许多可能的退出点,并且如果您开始编写捕获异常的代码(您在某些时候被迫做),然后它甚至通过你的代码创建了许多路径(在catch块中的代码,想想你需要日志工具而不是std :: cerr的服务器程序..)。 EH有优势,但这不是重点。 我的实际问题:

  • 你真的写了异常安全的代码吗?
  • 你确定你最后一次“准备就绪”的代码是异常安全吗?
  • 你甚至可以肯定,它是吗?
  • 您是否知道和/或实际使用可行的替代方案?
已邀请:

phic

赞同来自:

我尝试了最好的编写异常安全的代码,是的。 这意味着我要注意哪些线可以投掷。不是每个人都可以,并且牢记这一点至关重要。关键是要考虑并设计您的代码以满足标准中定义的异常保证。 可以编写此操作以提供强大的异常保证吗?我必须满足于基本的吗?哪些行可能会抛出异常,如何确保它们不会破坏对象?

ciste

赞同来自:

在C++中编写异常安全的代码并不是要使用大量的try {} catch {}块。它是关于记录代码提供什么样的保证。 我建议阅读Herb Sutter的Guru Of The Week系列,特别是分期59,60和61。 总而言之,您可以提供三种级别的异常安全性:

  • 基本:当您的代码抛出异常时,您的代码不会泄漏资源,并且对象仍然是可破坏的。
  • Strong:当您的代码抛出异常时,它会使应用程序的状态保持不变。
  • 不抛:您的代码永远不会抛出异常。
就个人而言,我发现这些文章的时间很晚,因此我的C++代码绝对不是例外。

cet

赞同来自:

首先(正如Neil所说),SEH是微软的结构化异常处理。它与C++中的异常处理类似但不完全相同。实际上,如果你想在Visual Studio中使用它,你必须使用enable C++ Exception Handling - 默认行为并不能保证在所有情况下都会销毁本地对象!在任何一种情况下,异常处理并不是真的更难,它只是不同。 现在为您提出实际问题。

Do you really write exception safe code?
是。在所有情况下,我都在争取异常安全代码。我使用RAII技术进行资源范围访问(例如,boost::shared_ptr用于内存,boost::lock_guard用于锁定)。通常,RAIIscope guarding技术的一致使用将使异常安全代码更容易编写。诀窍是了解存在的内容以及如何应用它。
Are you sure your last "production ready" code is exception safe?
不,这是安全的。我可以说,由于几年的24/7活动中的异常,我没有看到过程故障。我不希望完美的代码,只是编写良好的代码。除了提供异常安全性之外,上述技术还保证了使用try / catch块几乎无法实现的正确性。如果您正在捕获最高控制范围(线程,进程等)中的所有内容,那么您可以确保在面对异常时继续运行(most of the time)。在没有try / catch块的情况下,相同的技术也可以帮助您在异常情况下继续正常运行。
Can you even be sure that it is?
是。您可以通过彻底的代码审核来确定,但没有人真正做到这一点吗?定期的代码审查和细心的开发人员在很长的路要走。
Do you know and/or actually use alternatives that work?
多年来我尝试了一些变化,例如在高位编码状态(ala HRESULTs)或可怕的setjmp() ... longjmp()黑客。这两种方式在实践中都以完全不同的方式分解。
最后,如果你养成了应用一些技巧并仔细考虑在异常情况下可以实际执行某些操作的习惯的习惯,那么最终会得到非常安全的可读代码。您可以按照以下规则进行总结:
  • 您只想在可以对特定例外采取行动时看到try / catch
  • 您几乎不想在代码中看到原始的newdelete
  • Eschew std::sprintfsnprintf和一般数组 - 使用std::ostringstream格式化并用std::vector替换数组和std::string
  • 如有疑问,请在推出自己的
  • 之前在Boost或STL中查找功能
我只能建议您学习如何正确使用异常,如果计划用C++编写,请忘记结果代码。如果您想避免异常,可能需要考虑用另一种语言写入does not have themmakes them safe。如果您想真正学习如何充分利用C++,请阅读Herb SutterNicolai JosuttisScott Meyers中的一些书籍。

menim

赞同来自:

在“任何行都可以抛出”的假设下,不可能编写异常安全的代码。异常安全代码的设计主要依赖于您应该在代码中期望,观察,遵循和实现的某些合同/保证。保证永不抛出的代码是绝对必要的。还有其他种类的例外保证。 换句话说,创建异常安全代码在很大程度上是程序设计的问题,而不仅仅是普通编码问题。

dodit

赞同来自:

人们做了很多(我甚至会说最多)。 关于异常真正重要的是,如果你不编写任何处理代码 - 结果是非常安全和良好的行为。太急于恐慌,但安全。 你需要在处理程序中积极地犯错以获得不安全的东西,并且只有catch(...){}将与忽略错误代码进行比较。

uculpa

赞同来自:

  • 你真的写异常安全代码吗? [没有这样的事情。除非您拥有托管环境,否则例外是对错误的纸张屏障。这适用于前三个问题。]
  • 你知道和/或实际使用有效的替代品吗? [替代什么?这里的问题是人们不会将实际错误与正常的程序操作分开。如果是正常的程序操作(即找不到文件),则不是真正的错误处理。如果是实际错误,则无法“处理”它或者它不是实际错误。你的目标是找出问题所在并停止电子表格并记录错误,重新启动驱动程序到烤面包机,或者只是祈祷喷气式战斗机可以继续飞行,即使它的软件是错误的并希望最好。] < / LI>

nsit

赞同来自:

一般来说,EH很好。但是C++的实现并不是很友好,因为很难说你的异常捕获覆盖有多好。例如,Java使这很容易,如果你不处理可能的异常,编译器往往会失败。

nin

赞同来自:

我们中的一些人已经使用例外超过20年。例如,PL / I有它们。它们是一种新的危险技术的前提似乎对我来说是个问题。

nodio

赞同来自:

  • 你真的写异常安全代码吗?
好吧,我当然打算。
  • 您确定最后一个“生产就绪”代码是异常安全的吗?
我确信使用异常构建的24/7服务器可以全天候运行并且不会泄漏内存。
  • 你能确定吗,是吗?
很难确定任何代码是否正确。通常,人们只能通过结果
  • 你知道和/或实际使用有效的替代品吗?
没有。使用例外比我过去30年在编程中使用的任何替代方案更清晰,更容易。

gminus

赞同来自:

抛开SEH和C++异常之间的混淆,您需要意识到可以随时抛出异常,并在编写代码时考虑到这一点。对异常安全的需求在很大程度上推动了RAII,智能指针和其他现代C++技术的使用。 如果您遵循完善的模式,编写异常安全的代码并不是特别困难,事实上它比编写在所有情况下都能正确处理错误返回的代码更容易。

eenim

赞同来自:

我非常喜欢使用Eclipse和Java(Java新手),因为如果你缺少一个EH处理程序,它会在编辑器中抛出错误。这使得忘记处理异常变得更加困难...... 此外,使用IDE工具,它会自动添加try / catch块或其他catch块。

frerum

赞同来自:

我们中的一些人更喜欢像Java这样的语言,它迫使我们声明方法抛出的所有异常,而不是像C++和C#那样使它们不可见。 如果正确完成,异常优于错误返回代码,如果没有其他原因,您不必手动向上传播失败的调用链。 话虽这么说,低级API库编程应该可以避免异常处理,并坚持错误返回代码。 根据我的经验,用C++编写干净的异常处理代码很困难。我最终使用了new(nothrow)

oqui

赞同来自:

内容太长未翻译