简体   繁体   English

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

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

I tend to add lots of assertions to my C++ code to make debugging easier without affecting the performance of release builds.我倾向于在我的 C++ 代码中添加大量断言,以便在不影响发布版本性能的情况下更容易调试。 Now, assert is a pure C macro designed without C++ mechanisms in mind.现在, 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).另一方面,C++ 定义了std::logic_error ,它意味着在程序逻辑中存在错误的情况下抛出(因此得名)。 Throwing an instance might just be the perfect, more C++ish alternative to assert .抛出一个实例可能只是assert的完美的、更 C++ 风格的替代方案。

The problem is that assert and abort both terminate the program immediately without calling destructors, therefore skipping the cleanup, whereas throwing an exception manually adds unnecessary runtime costs.问题是assertabort都立即终止程序而不调用析构函数,因此跳过清理,而手动抛出异常会增加不必要的运行时成本。 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.解决这个问题的一种方法是创建一个自己的断言宏SAFE_ASSERT ,它的工作方式与 C 对应物一样,但在失败时抛出异常。

I can think of three opinions on this problem:对于这个问题,我能想到三个意见:

  • Stick to C's assert.坚持 C 的断言。 Since the program is terminated immediately, it does not matter whether changes are correctly unrolled.由于程序会立即终止,因此更改是否正确展开并不重要。 Also, using #define s in C++ is just as bad.此外,在 C++ 中使用#define也同样糟糕。
  • Throw an exception and catch it in main() .抛出异常并在 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().允许代码在程序的任何状态下跳过析构函数都是不好的做法,必须不惜一切代价避免,调用 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.终止程序的异常是可以的,并且由于NDEBUG ,这在发布版本中永远不会发生。 Catching is unnecessary and exposes implementation details of internal code to main() .捕获是不必要的,并将内部代码的实现细节暴露给main()

Is there a definitive answer to this problem?这个问题有明确的答案吗? Any professional reference?有专业参考吗?

Edited: Skipping destructors is, of course, no undefined behaviour.编辑:当然,跳过析构函数不是未定义的行为。

  • 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.如果断言被命中,你的代码需要被修复。

    CWE-617: Reachable Assertion CWE-617:可达断言

The product contains an assert() or similar statement that can be triggered by an attacker, which leads to an application exit or other behavior that is more severe than necessary.该产品包含可以被攻击者触发的 assert() 或类似语句,这会导致应用程序退出或其他比必要更严重的行为。

While assertion is good for catching logic errors and reducing the chances of reaching more serious vulnerability conditions, it can still lead to a denial of service.虽然断言有助于捕获逻辑错误并减少达到更严重漏洞情况的机会,但它仍然可能导致拒绝服务。

For example, if a server handles multiple simultaneous connections, and an assert() occurs in one single connection that causes all other connections to be dropped, this is a reachable assertion that leads to a denial of service.例如,如果服务器同时处理多个连接,并且在一个连接中发生 assert() 导致所有其他连接被丢弃,则这是导致拒绝服务的可达断言。

  • 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.例如,如果您提示用户输入一个数字并得到一些无法解析的信息,这是正常的,因为用户输入不受您的控制,您必须始终按照理所当然地处理所有可能的情况。 (Eg loop until you have a valid input, saying "Sorry, try again" in between.) (例如循环直到你有一个有效的输入,中间说“对不起,再试一次”。)

Assertions are entirely appropriate in C++ code.断言完全适用于 C++ 代码。 Exceptions and other error handling mechanisms aren't really intended for the same thing as assertions.异常和其他错误处理机制并不是真正用于与断言相同的事情。

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.断言用于诸如在通常不会检查 API 时检查 API 要求是否得到满足的事情,或者检查开发人员认为他通过构造保证的事情。 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.断言应始终表明程序运行不正确。


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

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.断言的真正好处(至少在 Linux 上)是核心转储是作为进程终止的结果生成的,因此您可以轻松地调查堆栈跟踪和变量。 This is much more useful to understand the logic failure than exception message.这对于理解逻辑失败比异常消息更有用。

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 中一样有用。断言不是用于错误处理,而是用于立即中止程序。

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 (eg a probability function that returns negative values ):开发人员的罪过(例如,返回负值的概率函数):

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

assert(probability() >= 0.0)断言(概率()> = 0.0)

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

int x = 1;整数 x = 1;

assert(x > 0);断言(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.这同样适用于硬件错误。

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

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