在comp.lang.c ++上有一个discussion。关于断言是否应该保存在生产代码中,以及是否应该在生产代码中保存在C ++中仅在调试版本中存在的断言。
显然,每个项目都是独一无二的,所以我的问题是不那么多是否应该保留断言,但在哪些情况下这个值得推荐/不是个好主意。
断言,我的意思是:
我不一定在谈论C或C ++。
我自己的观点是,如果你是程序员,但不拥有数据(大多数商业桌面应用程序就是这种情况),你应该保持它们,因为失败的断言显示了一个错误,而你不应该继续存在错误,存在破坏用户数据的风险。这会迫使您在发货前进行强力测试,并使错误更加明显,从而更容易发现并修复。
您的意见/体验是什么?
干杯,
卡尔
请参阅相关问题here
回复和更新
嘿格雷厄姆,断言是错误的,纯粹而简单,因此应该像一个一样处理。 由于应该在发布模式下处理错误,因此您不需要断言。
这就是为什么我在谈论断言时更喜欢“bug”这个词。它使事情更加清晰。对我来说,“错误”这个词太模糊了。丢失的文件是错误,而不是错误,程序应该处理它。试图取消引用空指针是一个错误,程序应该承认有些东西闻起来像坏奶酪。
因此,您应该使用断言来测试指针,但是存在具有正常错误处理代码的文件。
略微偏离主题,但讨论中的一个重点。
作为一个单挑,如果你的断言在失败时进入调试器,为什么不呢。但是有很多原因导致文件无法完全超出代码控制范围:读/写权限,磁盘已满,USB设备已拔下等等。由于您无法控制它,我觉得断言是不是处理这个问题的正确方法。
卡尔
托马斯,
是的,我有代码完成,并且必须说我非常不同意这个特定的建议。
假设您的自定义内存分配器搞砸了,并将一些仍然被其他对象使用的内存归零。我碰巧将这个对象定期解引用的指针归零,并且其中一个不变量是该指针永远不为空,并且你有几个断言以确保它保持这种状态。如果指针突然为空,你会怎么做?你只是()围绕它,希望它有效吗?
请记住,我们在这里讨论产品代码,因此不会破坏调试器并检查本地状态。这是用户机器上的一个真正的错误。
卡尔
答案 0 :(得分:76)
断言是不会过时的评论。他们记录了哪些理论状态是有意的,哪些状态不应该出现。如果代码被更改,因此状态允许更改,很快就会通知开发人员,并且需要更新断言。
答案 1 :(得分:54)
请允许我引用Steve McConnell的Code Complete。关于断言的部分是8.2。
通常,您不希望用户在生产代码中看到断言消息;断言主要用于开发和维护期间。断言通常在开发时编译到代码中,并从代码中编译生成。
但是,在同一部分的后面,给出了这样的建议:
对于高度健壮的代码,无论如何断言然后处理错误。
我认为只要性能不是问题,请将断言留在,而不是显示消息,让它写入日志文件。我认为这个建议也在Code Complete中,但我现在还没找到。
答案 2 :(得分:33)
在生产代码中打开断言,除非您已经测量程序在关闭时运行得更快。
如果不值得衡量以证明它更有效率,那么就不值得牺牲性能赌博的清晰度。“ - Steve McConnell 1993
答案 3 :(得分:17)
如果你甚至想在生产中留下断言,你可能会认为它们是错误的。断言的全部意义在于,您可以在生产中关闭它们,因为它们不是您解决方案的一部分。它们是一种开发工具,用于验证您的假设是否正确。但是你投入生产的时候,你应该对你的假设充满信心。
也就是说,有一种情况我将在生产中转换断言:如果我们在生产中遇到可重现的错误,我们在测试环境中很难再现,那么重现错误可能会有所帮助断言在生产中开启,看看它们是否提供了有用的信息。
一个更有趣的问题是:在您的测试阶段,何时关闭断言?
答案 4 :(得分:8)
断言永远不会保留在生产代码中。如果某个特定的断言似乎在生产代码中有用,那么它不应该是一个断言;它应该是运行时错误检查,即如下所示的代码:if( condition != expected ) throw exception
。
“断言”一词的意思是“只开发时间检查不在现场执行。”
如果你开始认为断言可能会进入现场,那么你将不可避免地开始制造其他危险的想法,比如想知道任何给定的断言是否真的值得做。没有不值得做出的断言。你永远不应该问自己“我应该断言这个吗?”你应该只问自己“有什么我忘了断言吗?”
答案 5 :(得分:6)
除非分析显示断言导致性能问题,否则我说它们也应该保留在生产版本中。
但是,我认为这也要求您在某种程度上优雅地处理断言失败。例如,它们应该产生一般类型的对话框,可以选择(自动)向开发人员报告问题,而不仅仅是退出或崩溃程序。此外,您应该注意不要将断言用于您实际允许的条件,但可能不喜欢或考虑不需要的条件。这些条件应该由代码的其他部分处理。
答案 6 :(得分:5)
在我的C ++中,我定义了REQUIRE(x),它类似于assert(x),只是如果断言在发布版本中失败,它会抛出异常。
由于断言失败表明存在错误,因此即使在发布版本中也应该认真对待。当我的代码性能很重要时,我经常会将REQUIRE()用于更高级别的代码,并将assert()用于必须快速运行的低级代码。如果失败条件可能是由第三方编写的代码传入的数据引起的,或者文件损坏,我也使用REQUIRE而不是断言(最好我会设计代码专门在文件损坏的情况下表现良好,但我们并不总是有时间这样做。)
他们说你不应该向最终用户展示这些断言消息,因为他们不会理解它们。所以?最终用户可能会向您发送一封带有屏幕截图的电子邮件或错误消息的某些文本,这有助于您进行调试。如果用户只是说“它崩溃了”,那么你修复它的能力就会降低。最好通过互联网自动发送断言失败消息给自己,但这只有在用户可以访问互联网并且您可以获得他们的许可时才有效。
答案 7 :(得分:4)
如果你想让他们用错误处理替换它们。没有什么比刚刚消失的程序更糟糕的了。我认为将某些错误视为严重错误并没有错,但是应该将它们定向到您的程序的一部分,该部分可以通过收集数据,记录它并通知用户您的应用程序有一些不需要的条件来处理它们。正在退出。
答案 8 :(得分:2)
如果处理它们与任何其他错误一样,我认为它没有问题。请记住,尽管C语言中的断言失败,与其他语言一样,只会退出程序,这对于生产系统来说通常是不够的。
有一些例外 - 例如,PHP允许您为断言失败创建自定义处理程序,以便您可以显示自定义错误,执行详细日志记录等,而不仅仅是退出。
答案 9 :(得分:2)
我们的数据库服务器软件包含生产和调试断言。调试断言就是这样 - 它们在生产代码中被删除。只有在(a)某些条件存在且从不存在且(b)不可能从该条件可靠地恢复时,才会发生生产断言。生产断言表明遇到了软件中的错误或发生了某种数据损坏。
由于这是一个数据库系统,我们正在存储潜在的企业关键数据,我们会尽一切努力避免数据损坏。如果存在可能导致我们存储不正确数据的情况,我们会立即断言,回滚所有事务并停止服务器。
话虽如此,我们也试图避免在性能关键的例程中产生断言。
答案 10 :(得分:1)
我认为断言是在线单元测试。在开发过程中用于快速测试很有用,但最终应该重构那些断言以在单元测试中进行外部测试。
答案 11 :(得分:1)
我发现最好处理范围内的所有错误,并使用断言来判断我们断言的假设是否为真。
即,如果你的程序正在打开/读取/关闭文件,那么无法打开文件就在范围内 - 这是一种真正的可能性,换句话说,这是疏忽的。因此,应该有与之关联的错误检查代码。
但是,假设您的fopen()被记录为始终返回有效的打开文件句柄。打开文件,并将其传递给readfile()函数。
在这种情况下,readfile函数可能根据其设计规范,可以假设它将获得一个有效的文件ptr。因此,在这样一个简单的程序中为负面情况添加错误处理代码将是浪费。但是,在继续执行之前,它至少应该以某种方式 - 以某种方式确保 - 实际上是这种情况来记录假设。它不应该实际上假设总是有效,以防它被错误地调用,或者它被复制/粘贴到其他程序中,例如。
所以,readfile(){assert(fptr!= NULL); ..}在这种情况下是合适的,而完全错误处理不是(忽略实际读取文件需要一些错误处理系统的事实)。
是的,这些断言应保留在生产代码中,除非绝对有必要禁用它们。即使这样,你也应该只在性能关键部分禁用它们。
答案 12 :(得分:0)
假设有一段代码正在生产中,并且它命中了一个通常会触发的断言。该断言发现了一个错误!除非没有,否则因为断言已关闭。
那么现在会发生什么?该程序要么(1)在问题的根源上进一步消失时以一种非信息性的方式崩溃,要么(2)顺利运行直至完成,可能会给出错误的结果。
这两种情况都不吸引人。即使在生产中也要使断言保持活动状态。
答案 13 :(得分:0)
ASSERTIONS不是错误,不应该作为错误处理。当抛出一个断言时,这意味着代码中存在错误,或者代码调用代码时也存在错误。
有几点可以避免在生产代码中启用断言: 1.您不希望最终用户看到诸如“ASSERTION failed MyPrivateClass.cpp line 147”之类的消息。最终用户不您是QA工程师。 2. ASSERTION可能会影响绩效
然而,有一个强有力的理由留下断言: ASSERTION可能会影响性能和时间,遗憾的是这有时很重要(特别是在嵌入式系统中)。
我倾向于投票支持在生产代码中保留断言但确保这些断言打印输出不会暴露给最终用户。
〜Yitzik
答案 14 :(得分:0)
大多数时候,当我在java中使用assertion(assert关键字)时,我会自动添加一些生产代码。根据这种情况,它可以是记录消息,例外......或者什么都不是。
据我所知,你所有的断言都对开发版本至关重要,而不是在生产中。其中一些必须保留,其他必须丢弃。
答案 15 :(得分:0)
我很少将断言用于编译时间类型检查的其他任何东西。我会使用异常而不是断言,因为大多数语言都是为处理它们而构建的。
我提供了一个例子
file = create-some-file();
_throwExceptionIf( file.exists() == false, "FILE DOES NOT EXIST");
针对
file = create-some-file();
ASSERT(file.exists());
应用程序如何处理断言?我更喜欢处理致命错误的旧try catch
方法。
答案 16 :(得分:-7)
断言是错误的,纯粹而简单,因此应该像一个一样处理。
由于应该在发布模式下处理错误,因此您不需要断言。
我看到断言的主要好处是条件中断 - 它们比通过VC的窗口钻取来设置需要1行代码的东西要容易得多。