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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12062365/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:50:41  来源:igfitidea点击:

Is using assert() in C++ bad practice?

c++coding-styleassert

提问by Fabian Knorr

I tend to add lots of assertions to my C++ code to make debugging easier without affecting the performance of release builds. Now, assertis a pure C macro designed without C++ mechanisms in mind.

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

C++ on the other hand defines std::logic_error, which is meant to be thrown in cases where there is an error in the program's logic (hence the name). Throwing an instance might just be the perfect, more C++ish alternative to assert.

另一方面std::logic_error,C++ 定义了,它意味着在程序逻辑出现错误的情况下抛出(因此得名)。抛出一个实例可能是assert.

The problem is that assertand abortboth terminate the program immediately without calling destructors, therefore skipping the cleanup, whereas throwing an exception manually adds unnecessary runtime costs. One way around this would creating an own assertion macro SAFE_ASSERT, which works just like the C counterpart, but throws an exception on failure.

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

I can think of three opinions on this problem:

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

  • Stick to C's assert.Since the program is terminated immediately, it does not matter whether changes are correctly unrolled. Also, using #defines in C++ is just as bad.
  • Throw an exception and catch it in main(). Allowing code to skip destructors in any state of the program is bad practice and must be avoided at all costs, and so are calls to terminate(). If exceptions are thrown, they must be caught.
  • Throw an exception and let it terminate the program.An exception terminating a program is okay, and due to NDEBUG, this will never happen in a release build. Catching is unnecessary and exposes implementation details of internal code to main().
  • 坚持 C 的断言。由于程序会立即终止,因此更改是否正确展开并不重要。此外,#define在 C++ 中使用s也同样糟糕。
  • 抛出异常并在 main() 中捕获它。允许代码在程序的任何状态下跳过析构函数都是不好的做法,必须不惜一切代价避免,调用 terminate() 也是如此。如果抛出异常,则必须捕获它们。
  • 抛出异常并让它终止程序。终止程序的异常是可以的,并且由于NDEBUG,这在发布版本中永远不会发生。捕获是不必要的,并将内部代码的实现细节暴露给main().

Is there a definitive answer to this problem? Any professional reference?

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

Edited:Skipping destructors is, of course, no undefined behaviour.

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

采纳答案by bames53

Assertions are entirely appropriate in C++ code. Exceptions and other error handling mechanisms aren't really intended for the same thing as assertions.

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

Error handling is for when there's a potential for recovering or reporting an error nicely to the user. For example if there's an error trying to read an input file you may want to do something about that. Errors could result from bugs, but they could also simply be the appropriate output for a given input.

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

Assertions are for things like checking that an API's requirements are met when the API wouldn't normally be checked, or for checking things the developer believes he's guaranteed by construction. For example if an algorithm requires sorted input you wouldn't normally check that, but you might have an assertion to check it so that debug builds flag that kind of bug. An assertion should always indicate an incorrectly operating program.

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



If you're writing a program where an unclean shutdown could cause a problem then you may want to avoid assertions. Undefined behavior strictly in terms of the C++ language doesn't qualify as such a problem here, since hitting an assertion is probably already the result of undefined behavior, or the violation of some other requirement which could prevent some clean-up from working properly.

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

Also if you implement assertions in terms of an exception then it could potentially be caught and 'handled' even though this contradicts the very purpose of the assertion.

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

回答by Kerrek SB

  • Assertions are for debugging. The user of your shipped code should never see them. If an assertion is hit, your code needs to be fixed.

  • Exceptions are for exceptional circumstances. If one is encountered, the user won't be able to do what she wants, but may be able to resume somewhere else.

  • Error handling is for normal program flow. For instance, if you prompt the user for a number and get something unparsable, that's normal, because user input is not under your control and you must always handle all possible situations as a matter of course. (E.g. loop until you have a valid input, saying "Sorry, try again" in between.)

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

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

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

回答by nogard

Assertions can be used to verify internal implementation invariants, like internal state before or after execution of some method, etc. If assertion fails it really means the logic of the program is broken and you can't recover from this. In this case the best you can do is to break as soon as possible without passing exception to the user. What is really nice about assertions (at least on Linux) is that core dump is generated as a result of process termination and thus you can easily investigate the stack trace and variables. This is much more useful to understand the logic failure than exception message.

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

回答by Jonathan Wakely

Not running destructors due to alling abort() is not undefined behaviour!

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

If it were, then it would be undefined behaviour to call std::terminate()too, and so what would be the point in providing it?

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

assert()is just as useful in C++ as in C. Assertions are not for error handling, they're for aborting the program immediately.

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

回答by FranMowinckel

IMHO, assertions are for checking conditions that if violated, make everything else nonsense. And therefore you cannot recover from them or rather, recovering is irrelevant.

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

I would group them into 2 categories:

我将它们分为两类:

  • Developer sins (e.g. a probability function that returns negative values ):
  • 开发人员的罪过(例如,返回负值的概率函数):

float probability() { return -1.0; }

assert(probability() >= 0.0)

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

断言(概率()> = 0.0)

  • The Machine is broken (e.g. the machine which runs your program is very wrong):
  • 机器坏了(例如,运行您程序的机器非常错误):

int x = 1;

assert(x > 0);

整数 x = 1;

断言(x > 0);

These are both trivial examples but not too far from reality. For example, think about naive algorithms that return negative indexes for using with vectors. Or embedded programs in custom hardware. Or rather because sh*t happens.

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

And if there is such development mistakes you should not be confident about any recovering or error handling mechanism implemented. The same applies for hardware errors.

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