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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在C ++中使用assert()是不好的做法?相关的知识,希望对你有一定的参考价值。

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

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

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

我可以想到关于这个问题的三种看法:

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

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

编辑:跳过析构函数当然没有未定义的行为。

答案

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

错误处理是指有可能很好地恢复或向用户报告错误。例如,如果尝试读取输入文件时出错,您可能想要对此进行一些操作。错误可能是由错误引起的,但它们也可能只是给定输入的适当输出。

断言用于检查在通常不检查API时是否满足API的要求,或用于检查开发人员认为通过构造保证的事情。例如,如果一个算法需要排序输入,你通常不会检查它,但你可能有一个断言来检查它,以便debug build标记那种bug。断言应始终指示错误操作的程序。


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

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

另一答案
  • 断言用于调试。您提供的代码的用户永远不应该看到它们。如果一个断言命中,你的代码需要修复。
  • 例外情况适用于特殊情况。如果遇到一个,用户将无法做她想做的事,但可能能够在其他地方恢复。
  • 错误处理是针对正常的程序流程。例如,如果您提示用户输入数字并获得不可分类的内容,这是正常的,因为用户输入不在您的控制之下,您必须始终处理所有可能的情况。 (例如循环,直到你有一个有效的输入,说“抱歉,再试一次”。)
另一答案

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

另一答案

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

如果是,那么调用std::terminate()也是不明确的行为,那么提供它的重点是什么呢?

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

另一答案

恕我直言,断言是为了检查如果违反的条件,使其他一切无稽之谈。因此,你无法从他们身上恢复,或者更确切地说,恢复是无关紧要的。

我会将它们分为两类:

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

float probability(){return -1.0; }

断言(probability()> 0.0)

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

int x = 1;

断言(x> 0);

这些都是微不足道的例子,但离现实并不太远。例如,考虑使用向量返回负索引的朴素算法。或者在自定义硬件中嵌入程序。或者说因为狗屎发生了。

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

以上是关于在C ++中使用assert()是不好的做法?的主要内容,如果未能解决你的问题,请参考以下文章

在 void 方法中使用 return 是不好的做法吗?

在多个 for 循环中使用相同的变量名是不好的做法吗?

在 C 中使用 short 是一种不好的做法

为什么在OOP中使用break / continue标签是不好的做法(例如Java,C#)? [关闭]

在sql查询中使用局部变量作为开关是不好的做法

在 Angular 中使用 jQuery 是一种不好的做法吗? [关闭]