问答中心分类: C++您(真的)编写异常安全代码吗?
0
Frunsi 提问 3分钟 前

就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、参考资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,访问帮助中心寻求指导。

关闭9 年前.

异常处理(EH)似乎是当前的标准,通过搜索网络,我找不到任何试图改进或替换它的新想法或方法(嗯,存在一些变化,但没有什么新奇的)。
尽管大多数人似乎忽略它或只是接受它,EH一些巨大的缺点:异常对代码是不可见的,它会创建很多很多可能的退出点。乔尔在软件上写了一篇关于它的文章.比较goto完美契合,这让我再次想到了 EH。
我尽量避免使用 EH,只使用返回值、回调或任何适合目的的东西。但当你必须编写可靠的代码时,现在你不能忽视 EH: 它开始于new,这可能会引发异常,而不是只返回 0(就像过去一样)。这使得任何 C++ 代码行易受伤害的到一个例外。然后 C++ 基础代码中更多的地方抛出异常…… std lib 会这样做,依此类推。
这感觉就像在摇摇欲坠的土地上行走.. 所以,现在我们不得不关心异常!
但是很难,真的很难。您必须学习编写异常安全代码,即使您有一些经验,仍然需要仔细检查任何单行代码以确保安全!或者你开始在任何地方放置 try/catch 块,这会使代码变得混乱,直到它达到不可读的状态。
EH 用一种在代码中创建许多可能的退出点的方法取代了旧的干净的确定性方法(返回值……被迫在某些时候这样做),然后它甚至会通过您的代码创建大量路径(catch 块中的代码,考虑一个服务器程序,您需要除 std::cerr 之外的日志记录工具 ..)。 EH 有优势,但这不是重点。
我的实际问题:

  • 你真的会写异常安全的代码吗?
  • 您确定您的最后一个“生产就绪”代码是异常安全的吗?
  • 你能确定吗?
  • 您知道和/或实际使用有效的替代方案吗?
rlbond 回复 3分钟 前

“EH 取代了旧的干净确定性方法(返回值..)”什么?异常与返回码一样具有确定性。他们没有什么随机的。

rlbond 回复 3分钟 前

EH 长期以来一直是标准(甚至从 Ada 开始!)

rlbond 回复 3分钟 前

“EH”是异常处理的既定缩写吗?我以前从未见过它。

rlbond 回复 3分钟 前

@Thomas Padron-McCarthy:您还会如何缩写 _E_xception _H_andling?我的问题很长,我不想阻止人们阅读它,所以我将大部分时间使用的术语缩写了..好吧,我首先使用了错误的术语,但是……嘿,我为什么要回答无论如何,你只是想迷惑我,不是吗? 😀

rlbond 回复 3分钟 前

牛顿定律在今天适用于大多数事情。几个世纪以来,他们已经预测了非常广泛的现象,这一事实意义重大。

rlbond 回复 3分钟 前

@David Thornley:是的,是的,返回错误代码和异常处理工作了多年,下一步是什么?

rlbond 回复 3分钟 前

@frunsi:嘿,很抱歉让您感到困惑!但我认为,如果有的话,一个奇怪的、无法解释的首字母缩略词或缩写会更令人沮丧。

rlbond 回复 3分钟 前

在 C++ 中有一个关于异常安全编码的非常好的视频异常安全代码.com

rlbond 回复 3分钟 前

C++ 中的异常安全编码 — Jon Kalb;这篇演讲甚至专门引用了 Joel 的文章,并说明了他的例子哪里出了问题。

12 Answers
0
Joh 回答 3分钟 前

在 C++ 中编写异常安全代码与使用大量 try { } catch { } 块无关。它是关于记录您的代码提供什么样的保证。
我推荐阅读 Herb Sutter 的本周大师系列,特别是第 59、60 和 61 期。
总而言之,您可以提供三个级别的异常安全性:

  • 基本:当您的代码抛出异常时,您的代码不会泄漏资源,并且对象仍然是可破坏的。
  • 强:当您的代码抛出异常时,它使应用程序的状态保持不变。
  • 不抛出:您的代码从不抛出异常。

就个人而言,我发现这些文章很晚,所以我的很多 C++ 代码绝对不是异常安全的。

Frunsi 回复 3分钟 前

他的书“Exceptional C++”也很好读。但我仍然试图质疑 EH 的概念……

jk. 回复 3分钟 前

+1 OP 将处理异常(捕获它们)与异常安全(通常更多关于 RAII)混为一谈

Raedwald 回复 3分钟 前

我怀疑很少有生产 C++ 代码是异常安全的。

0
bmargulies 回答 3分钟 前

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

Frunsi 回复 3分钟 前

请不要误会我的意思,我正在(或正在尝试)质疑 EH。尤其是 C++ EH。我正在寻找替代品。也许我必须接受它(如果这是唯一的方法,我会接受),但我认为可能有更好的选择。不是我认为这个概念是新的,而是是的,我认为能够比使用返回码进行显式错误处理更危险…

bmargulies 回复 3分钟 前

如果您不喜欢它,请不要使用它。将 try 块放在您需要调用的可以抛出的东西周围,并重新发明 ye-olde-error-code,并遭受它所具有的问题,这在某些情况下是可以容忍的。

Frunsi 回复 3分钟 前

好的,完美,我将只使用 EH 和错误代码,并接受它。我是个傻子,我应该自己解决这个问题! 😉

0
D.Shawley 回答 3分钟 前

首先(正如尼尔所说),SEH 是微软的结构化异常处理。它与 C++ 中的异常处理类似但不完全相同。事实上,你必须启用 C++ 异常处理如果您希望在 Visual Studio 中使用它 – 默认行为并不能保证在所有情况下都会销毁本地对象!无论哪种情况,异常处理都不是真的更难这只是不同的.
现在针对您的实际问题。

你真的会写异常安全的代码吗?

是的。在所有情况下,我都努力争取异常安全的代码。我使用 RAII 技术来宣传对资源的范围访问(例如,boost::shared_ptr为了记忆,boost::lock_guard用于锁定)。一般来说,一致的使用RAII范围保护技术将使异常安全代码更容易编写。诀窍是了解存在的内容以及如何应用它。

您确定您的最后一个“生产就绪”代码是异常安全的吗?

不,它和它一样安全。我可以说,在几年的 24/7 活动中,我没有看到由于异常而导致的流程错误。我不期望完美的代码,只期望编写良好的代码。除了提供异常安全之外,上述技术还以几乎不可能实现的方式保证正确性try/catch块。如果您正在捕获顶级控制范围内的所有内容(线程、进程等),那么您可以确保在遇到异常时您将继续运行(大多数时候)。同样的技巧也会帮助你继续跑步正确地面对例外没有try/catch到处都是块.

你能确定它是吗?

是的。您可以通过彻底的代码审计来确定,但没有人真的这样做吗?不过,定期的代码审查和细心的开发人员对于实现这一目标还有很长的路要走。

您知道和/或实际使用有效的替代方案吗?

多年来我尝试了一些变化,例如高位的编码状态(alaHRESULTs) 或者那个可怕的setjmp() ... longjmp()黑客。尽管以完全不同的方式,这两者在实践中都被打破了。

最后,如果您养成应用一些技术的习惯,并仔细考虑在哪里可以实际做某事来响应异常,您最终会得到非常易读且异常安全的代码。您可以按照以下规则进行总结:

  • 你只想看try/catch当您可以对特定异常做某事时
  • 你几乎从不想看到一个原始的new或者delete在代码中
  • 避开std::sprintf,snprintf, 和一般的数组 – 使用std::ostringstream用于格式化和替换数组std::vectorstd::string
  • 如有疑问,请先在 Boost 或 STL 中查找功能,然后再自行开发

如果您打算用 C++ 编写,我只能建议您学习如何正确使用异常并忘记结果代码。如果您想避免异常,您可能需要考虑用另一种语言编写没有它们或者让他们安全.如果您想真正学习如何充分利用 C++,请阅读以下几本书赫伯萨特,尼古拉·约苏蒂斯, 和斯科特迈耶斯.

Raedwald 回复 3分钟 前

“默认行为并不能保证在所有情况下都销毁本地对象”您是说 Visual Studio C++ 编译器的默认设置生成的代码是不正确面对例外。真的是这样吗?

Raedwald 回复 3分钟 前

“你几乎从不想看到一个生的new或者delete在代码中“:通过生的我猜你的意思是在构造函数或析构函数之外。

D.Shawley 回复 3分钟 前

@Raedwald – 回复:VC++:当抛出 SEH 异常时,VS2005 版本的 VC++ 不会破坏本地对象。阅读“启用 C++ 异常处理”.在VS2005中,SEH异常默认不调用C++对象的析构函数。如果您调用 Win32 函数或在 C 接口 DLL 中定义的任何内容,那么您必须担心这一点,因为它们可以(并且有时会)以您的方式抛出 SEH 异常。

D.Shawley 回复 3分钟 前

@Raedwald:回复:生的: 基本上,delete永远不应该在执行之外使用tr1::shared_ptr之类的。new可以使用,前提是它的用法类似于tr1::shared_ptr<X> ptr(new X(arg, arg));.重要的部分是结果new直接进入托管指针。这上页boost::shared_ptr最佳实践描述最好的。

mabraham 回复 3分钟 前

为什么在您的异常安全规则集中引用 std::sprintf (et al)?这些没有记录他们抛出任何异常,例如en.cppreference.com/w/cpp/io/c/fprintf

0
AnT stands with Russia 回答 3分钟 前

在“任何行都可以抛出”的假设下编写异常安全的代码是不可能的。异常安全代码的设计严重依赖于您应该在代码中期望、遵守、遵循和实施的某些合同/保证。绝对有必要保证代码绝不扔。还有其他类型的异常保证。
换句话说,创建异常安全代码在很大程度上是程序问题设计不仅仅是简单的问题编码.

0
anon 回答 3分钟 前
  • 你真的会写异常安全的代码吗?

好吧,我当然打算这样做。

  • 您确定您的最后一个“生产就绪”代码是异常安全的吗?

我确信我使用异常构建的 24/7 服务器可以 24/7 运行并且不会泄漏内存。

  • 你能确定吗?

很难确定任何代码都是正确的。通常,一个人只能通过结果

  • 您知道和/或实际使用有效的替代方案吗?

不。使用异常比我过去 30 年在编程中使用的任何替代方法都更干净、更容易。

Matt Joiner 回复 3分钟 前

这个答案没有价值。

Miles Rout 回复 3分钟 前

@MattJoiner 那么这个问题没有价值。