我曾经在一家公司工作,其中一些主要架构师/开发人员已经强制要求断言不使用的各种项目,并且他们通常会从代码中删除并替换为异常。
我觉得他们在编写正确的代码时非常重要。任何人都可以建议如何证明这样的授权是合理的吗?如果是这样,断言有什么问题?
答案 0 :(得分:20)
根据JaredPar的评论,我们使用assert的修改版本,就像合同一样。此版本已编译到发行版代码中,因此开销较小,但除非设置了诊断开关,否则将禁用,从而最大限度地降低性能开销。我们在此实例中的断言处理程序可以设置为禁用,静默模式(例如,记录到文件)或噪声模式(例如,在屏幕上显示abort / ignore,其中abort抛出异常)。
我们使用自动回归测试作为我们的预发布测试的一部分,并且断言在这里非常重要,因为它们允许我们找到在GUI级别无法获取的潜在内部错误,并且可能最初不会致命。用户级别。通过自动化,我们可以使用和不使用诊断运行测试,除了执行时间之外几乎没有开销,因此我们还可以确定断言是否有任何其他副作用。
断言要注意的一件事是副作用。例如,您可能会看到类似 assert(MyDatabasesIsOk())的内容,这会无意中纠正数据库中的错误。这是一个错误,因为断言永远不会改变正在运行的应用程序的状态。
答案 1 :(得分:6)
关于断言我唯一真正负面的是它们不会在零售代码中运行。在我们的团队中,我们倾向于避免断言。相反,我们使用契约,这是在零售和调试中运行的断言。
我们现在使用断言的唯一时间是以下其中一个是真的。
答案 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)
注意,在析构函数中抛出异常是未定义的行为。