20

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

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

4

11 回答 11

20

根据 JaredPar 的评论,我们使用了一个修改版本的断言,它就像一个合同。此版本被编译到发布代码中,因此存在较小的开销,但除非设置了诊断开关,否则将被禁用,从而将性能开销降至最低。在这种情况下,我们的断言处理程序可以设置为禁用、静默模式(例如,记录到文件)或嘈杂模式(例如,使用 abort/ignore 在屏幕上显示,abort 会引发异常)。

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

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

于 2009-01-07T08:19:12.333 回答
6

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

我们现在唯一使用断言的情况是以下条件之一是否为真。

  1. 断言代码具有着的性能影响
  2. 特定情况不是致命的
  3. 偶尔会有一段代码可能会死也可能不会死。我们将添加一个断言,基本上说“你是怎么到这里的”。不触发并不意味着代码确实已经死了,但是如果 QA 给我发电子邮件并说“这个断言是什么意思”,我们现在有一个重现来获取特定的代码(当然它会立即记录下来)。
于 2009-01-07T06:44:49.763 回答
5

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

断言用于不应该发生的状态。例如,signalton 指针永远不应该为空,并且应该在开发过程中使用断言来发现这个错误。处理它有一个例外是更多的工作。

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

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

于 2009-01-07T06:41:47.960 回答
4

这取决于您系统的关键程度:断言是一种故障快速策略,而当系统可以执行某种恢复时可以使用异常。

例如,我不会在银行应用程序或电信系统中使用断言:我会抛出一个异常,该异常将在调用堆栈中被捕获。在那里,可以清理资源,处理下一个调用/事务;只有一个会丢失。

于 2009-01-07T09:48:55.493 回答
3

断言是一件很棒的事情,但不要与参数/返回值检查混淆。您在您认为不会发生的情况下使用它们,而不是在您期望可能发生的情况下使用它们。

我最喜欢使用它们的地方是在真正不应该到达的代码块中 - 例如在一个枚举上的defaultcase in switch-statementcase对每个可能的枚举值都有一个。

您可能会使用新值扩展枚举,但不要更新所有switch涉及枚举的语句,这是相对常见的,您会想尽快知道这一点。在这种情况下,快速失败是你最好的愿望。

诚然,在那些地方,您通常也需要在生产构建中中断的东西。但abort()强烈推荐这种情况下的ing原则。调试器中良好的堆栈跟踪为您提供了修复错误的信息,而不是猜测。

于 2009-01-07T10:08:17.653 回答
1

调试版本中确实存在断言,但发布版本中不存在断言吗?

如果您想验证/断言某些内容,您不想在发布版本和调试版本中这样做吗?

于 2009-01-07T06:41:01.690 回答
1

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

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

于 2009-01-07T06:53:08.983 回答
1

断言可以简单地通过不定义 NDEBUG 来保留,所以这不是一个真正的问题。

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

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

于 2009-01-07T08:43:35.593 回答
1

我们使用断言来记录假设

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

于 2009-01-07T11:21:54.290 回答
0

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

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

根据团队的技能水平,引导他们远离危险区域可能是个好主意。

于 2009-01-07T10:11:17.397 回答
0

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

于 2009-02-26T14:12:00.070 回答