断言总是坏的吗?

时间:2009-01-07 06:37:20

标签: c++ exception assertions

我曾经在一家公司工作,其中一些主要架构师/开发人员已经强制要求断言不使用的各种项目,并且他们通常会从代码中删除并替换为异常。

我觉得他们在编写正确的代码时非常重要。任何人都可以建议如何证明这样的授权是合理的吗?如果是这样,断言有什么问题?

11 个答案:

答案 0 :(得分:20)

根据JaredPar的评论,我们使用assert的修改版本,就像合同一样。此版本已编译到发行版代码中,因此开销较小,但除非设置了诊断开关,否则将禁用,从而最大限度地降低性能开销。我们在此实例中的断言处理程序可以设置为禁用,静默模式(例如,记录到文件)或噪声模式(例如,在屏幕上显示abort / ignore,其中abort抛出异常)。

我们使用自动回归测试作为我们的预发布测试的一部分,并且断言在这里非常重要,因为它们允许我们找到在GUI级别无法获取的潜在内部错误,并且可能最初不会致命。用户级别。通过自动化,我们可以使用和不使用诊断运行测试,除了执行时间之外几乎没有开销,因此我们还可以确定断言是否有任何其他副作用。

断言要注意的一件事是副作用。例如,您可能会看到类似 assert(MyDatabasesIsOk())的内容,这会无意中纠正数据库中的错误。这是一个错误,因为断言永远不会改变正在运行的应用程序的状态。

答案 1 :(得分:6)

关于断言我唯一真正负面的是它们不会在零售代码中运行。在我们的团队中,我们倾向于避免断言。相反,我们使用契约,这是在零售和调试中运行的断言。

我们现在使用断言的唯一时间是以下其中一个是真的。

  1. 断言代码具有明显的性能影响
  2. 特殊情况并非致命
  3. 偶尔会有一段代码可能已经死亡,也可能没有。我们将添加一个断言,基本上说“你是怎么来到这里的”。不解雇并不意味着代码确实已经死了但是如果QA给我发电子邮件并说“这个断言意味着什么”,我们现在有一个复制品来获取特定的代码(当然会立即记录)。

答案 2 :(得分:5)

断言和异常用于两个不同的事情。

断言用于不应发生的状态。例如,signalton指针永远不应为null,并且应该在开发期间使用assert拾取此错误。处理它有一个例外是没有更多的工作。

另一方面,异常用于在应用程序的正常运行中可能发生的罕见状态。例如,使用fopen并返回一个空指针。它可能会发生,但大多数时候它会返回一个有效的指针。

使用断言是错误的也不是正确的,但它归结为个人偏好,因为在一天结束时,它是一种使编程更容易并且可以被其他工具取代的工具。

答案 3 :(得分:3)

这取决于系统的重要性:断言是 failfast策略,而系统可以执行某种恢复时可以使用异常。

例如,我不会在银行应用程序或电信系统中使用断言:我会抛出一个异常,它会在调用堆栈中占据上层。在那里,可以清理资源,并且可以处理下一个呼叫/交易;只有一个会丢失。

答案 4 :(得分:3)

断言是一件好事,但不要与参数/返回值检查混淆。您可以在不相信的情况下使用它们,而不是在您预期可能发生的情况下使用它们。

我最喜欢使用它们的地方是真正不应该访问的代码块 - 例如default中的switch个案例 - 对于case枚举的枚举每个可能的枚举值。

您可以使用新值扩展枚举,但不更新涉及枚举的所有switch语句,这是相对常见的,您需要尽快知道。在这种情况下,失败是最好的。

当然,在那些地方你通常也想要在生产版本中打破的东西。但强烈建议在这种条件下abort()的原则。调试器中的良好堆栈跟踪为您提供了比猜测更快地修复错误的信息。

答案 5 :(得分:1)

调试版本中是否存在断言,但发布版本中是否存在?

如果要验证/断言某些内容,是否要在发布版本和调试版本中执行此操作?

答案 6 :(得分:1)

唯一的猜测是,因为异常通常是非致命的,所以它使得代码库不会在某种奇怪状态下死亡。反驳的是,断言的死亡率指向问题所在,从而易于调试。

我个人更喜欢承担断言的风险,因为我觉得它会导致更容易调试的更可预测的代码。

答案 7 :(得分:1)

我们使用断言记录假设

我们在代码审查中确保在断言中不执行任何应用程序逻辑,因此在发布之前不久就将它们关闭是非常安全的。

答案 8 :(得分:0)

仅仅通过不定义NDEBUG就可以断言断言,所以这不是一个真正的问题。

真正的问题是断言调用abort(),它会立即停止程序。如果您的程序在退出之前必须进行严格的清理,这可能会导致问题。异常具有以下优点:即使异常从未被捕获,也可以正确调用析构函数。

因此,在清理确实很重要的情况下,例外情况更合适。否则,断言就好了。

答案 9 :(得分:0)

否决assert()的一个原因是,可以编写在定义NDEBUG时正常工作的代码,但在未定义NDEBUG时失败。反之亦然。

这是一个陷阱,好的程序员不应该经常陷入困境,但有时原因可能非常微妙。例如,assert()中的代码可能会推动可执行文件中的内存分配或代码位置,以便发生可能发生的分段错误(反之亦然)。

根据团队的技能水平,将他们从风险区域引导是一个好主意。

答案 10 :(得分:0)

注意,在析构函数中抛出异常是未定义的行为。