繁体   English   中英

在 C++ 中使用 assert() 是不好的做法吗?

[英]Is using assert() in C++ bad practice?

我倾向于在我的 C++ 代码中添加大量断言,以便在不影响发布版本性能的情况下更容易调试。 现在, assert是一个纯 C 宏,设计时没有考虑 C++ 机制。

另一方面,C++ 定义了std::logic_error ,它意味着在程序逻辑中存在错误的情况下抛出(因此得名)。 抛出一个实例可能只是assert的完美的、更 C++ 风格的替代方案。

问题是assertabort都立即终止程序而不调用析构函数,因此跳过清理,而手动抛出异常会增加不必要的运行时成本。 解决这个问题的一种方法是创建一个自己的断言宏SAFE_ASSERT ,它的工作方式与 C 对应物一样,但在失败时抛出异常。

对于这个问题,我能想到三个意见:

  • 坚持 C 的断言。 由于程序会立即终止,因此更改是否正确展开并不重要。 此外,在 C++ 中使用#define也同样糟糕。
  • 抛出异常并在 main() 中捕获它 允许代码在程序的任何状态下跳过析构函数都是不好的做法,必须不惜一切代价避免,调用 terminate() 也是如此。 如果抛出异常,则必须捕获它们。
  • 抛出异常并让它终止程序。 终止程序的异常是可以的,并且由于NDEBUG ,这在发布版本中永远不会发生。 捕获是不必要的,并将内部代码的实现细节暴露给main()

这个问题有明确的答案吗? 有专业参考吗?

编辑:当然,跳过析构函数不是未定义的行为。

  • 断言用于调试 您所提供代码的用户不应看到它们。 如果断言被命中,你的代码需要被修复。

    CWE-617:可达断言

该产品包含可以被攻击者触发的 assert() 或类似语句,这会导致应用程序退出或其他比必要更严重的行为。

虽然断言有助于捕获逻辑错误并减少达到更严重漏洞情况的机会,但它仍然可能导致拒绝服务。

例如,如果服务器同时处理多个连接,并且在一个连接中发生 assert() 导致所有其他连接被丢弃,则这是导致拒绝服务的可达断言。

  • 例外是针对特殊情况的 如果遇到,用户将无法做她想做的事,但可以在其他地方继续。

  • 错误处理适用于正常的程序流程。 例如,如果您提示用户输入一个数字并得到一些无法解析的信息,这是正常的,因为用户输入不受您的控制,您必须始终按照理所当然地处理所有可能的情况。 (例如循环直到你有一个有效的输入,中间说“对不起,再试一次”。)

断言完全适用于 C++ 代码。 异常和其他错误处理机制并不是真正用于与断言相同的事情。

错误处理适用于有可能向用户很好地恢复或报告错误的情况。 例如,如果尝试读取输入文件时出现错误,您可能需要对此做一些事情。 错误可能由错误导致,但它们也可能只是给定输入的适当输出。

断言用于诸如在通常不会检查 API 时检查 API 要求是否得到满足的事情,或者检查开发人员认为他通过构造保证的事情。 例如,如果算法需要排序输入,您通常不会检查它,但您可能有一个断言来检查它,以便调试构建标记那种错误。 断言应始终表明程序运行不正确。


如果您正在编写一个程序,其中不正常关闭可能会导致问题,那么您可能希望避免断言。 严格按照 C++ 语言的未定义行为在这里不属于此类问题,因为命中断言可能已经是未定义行为的结果,或者违反了一些其他要求,这可能会阻止某些清理工作正常进行。

此外,如果您根据异常实现断言,那么它可能会被捕获并“处理”,即使这与断言的目的相矛盾。

断言可用于验证内部实现不变量,例如某些方法执行之前或之后的内部状态等。如果断言失败,则确实意味着程序逻辑已损坏,您无法从中恢复。 在这种情况下,您能做的最好的事情就是尽快中断而不将异常传递给用户。 断言的真正好处(至少在 Linux 上)是核心转储是作为进程终止的结果生成的,因此您可以轻松地调查堆栈跟踪和变量。 这对于理解逻辑失败比异常消息更有用。

由于 alling abort() 而没有运行析构函数并不是未定义的行为!

如果是,那么调用std::terminate()也是未定义的行为,那么提供它有什么意义呢?

assert()在 C++ 中和在 C 中一样有用。断言不是用于错误处理,而是用于立即中止程序。

恕我直言,断言是为了检查条件,如果被违反,其他一切都是废话。 因此,您无法从它们中恢复,或者更确切地说,恢复是无关紧要的。

我将它们分为两类:

  • 开发人员的罪过(例如,返回负值的概率函数):

浮动概率(){返回-1.0; }

断言(概率()> = 0.0)

  • 机器坏了(例如,运行您程序的机器非常错误):

整数 x = 1;

断言(x > 0);

这些都是微不足道的例子,但与现实相差不远。 例如,考虑返回负索引以用于向量的朴素算法。 或定制硬件中的嵌入式程序。 或者更确切地说,因为sh*t 发生了

如果存在此类开发错误,您不应该对实施的任何恢复或错误处理机制充满信心。 这同样适用于硬件错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM