Mat*_* M. 36
背景:我为了生活而编写服务器软件,这种软件在加载下一个版本之前会持续数周.所以我的答案可能会与高度防守的代码联系起来.
原则.
在我们深入研究使用地点的具体细节之前assert,了解其背后的原理非常重要.
assert是防御性编程中必不可少的工具.它有助于验证假设(实际上断言它们),从而捕获编程错误(区别于用户错误).目标assert是检测错误情况,通常不能立即恢复.
例:
char const* strstr(char const* haystack, char const* needle) {
assert(haystack); assert(needle);
// ...
}
Run Code Online (Sandbox Code Playgroud)
备择方案.
在C?没有其他选择.除非您的功能被设计为能够传递错误代码或返回标记值,并且这是适当记录的.
在C++中,异常是完全可以接受的替代方案.但是,assert可以帮助生成内存转储,以便在检测到错误情况时(这有助于调试)可以准确查看程序所处的状态,而异常将展开堆栈,从而丢失上下文(oups. ..).
另外,一个异常可能(不幸的是)被高级处理程序(或者同事开发者的一个令人讨厌的捕获)抓住(当然你不会这样做),在这种情况下你可能会完全错过错误,直到为时已晚.
哪里不使用它.
首先,应该理解assert它只在Debug代码中有用.在Release中,定义了NDEBUG,不生成任何代码.作为推论,在Release中assert具有与评论相同的价值.
其次,应该理解,畸形的输入是你生活的一部分.您是否希望assert每次发生错误时编译器都显示一条消息?哼!因此:
第三,应该理解的是崩溃的不是赞赏.您的程序预计会顺利运行.因此,人们不应该试图在发布模式下保持断言:发布代码最终会在最终用户手中结束,永远不会崩溃.在最坏的情况下,它应该在显示错误消息时关闭.据预计,在此过程中没有用户数据丢失,甚至更好,如果在重新启动后,用户被返回到原来的地方:那就是现代的浏览器做的,例如.
注意:对于服务器代码,在"命中"断言时,我们设法在大多数情况下回到适当的位置来处理下一个查询.
在哪里使用它.
assert在调试模式下,因此应该用于调试.每当您测试新代码时,无论何时运行测试套件,只要软件在您(或您的团队成员)手中,无论何时软件都在您的QA部门手中.断言可让您发现错误并为您提供错误的完整上下文,以便您可以修复.
更好.由于您知道代码不会在Release中执行,因此您可以负担得起执行昂贵的检查.
注意:如果仅检查性能,还应测试Release二进制文件.
并在发布?
好吧,在我工作的代码库中,我们用特定的异常替换廉价的断言(其他被忽略),这些异常仅由记录问题的高级处理程序捕获(带有回溯),返回预编码的错误响应和恢复服务.开发团队会自动得到通知.
在部署的软件中,我所看到的最佳实践意味着创建内存转储并将其流回开发人员进行分析,同时尝试不丢失任何用户数据并尽可能忠实地对待不幸的用户.当我考虑到这项任务的难度时,我觉得在服务器端工作真的很幸运;)
我要抛弃我的看法assert().我可以找到assert()其他地方的功能,但stackoverflow提供了一个很好的论坛,可以提供有关如何以及何时使用它的建议.
无论assert和static_assert服务类似的功能.假设你有一些功能foo.例如,假设您有一个foo(void*)假定其参数不为null 的函数:
void foo(void* p) {
assert(p);
...
}
Run Code Online (Sandbox Code Playgroud)
你的功能有几个人关心它.
首先,调用您的函数的开发人员.他可能只是看看你的文档,也许他会错过关于不允许空指针作为参数的部分.他可能永远不会读取该函数的代码,但是当他在调试模式下运行它时,断言可能会捕获他对函数的不恰当使用(特别是如果他的测试用例很好).
第二个(也是更重要的)是读取代码的开发人员.对他来说,你的断言说在这一行之后,p不是空的.这有点被忽略了,但我相信这是assert宏最有用的功能.它记录并执行条件.
assert只要可行,您应该使用s对此信息进行编码.我喜欢把它看成是说"在代码中的这一点,这是真的"(这在某种程度上说,这使比评论会更强).当然,如果这样的陈述实际上没有传达太多/任何信息,那么就不需要它.