断言(assert宏)的副作用

我的确对#define的很多种用法都深恶痛绝,唯对定义在中的assert宏情有独钟。说句实话,我鼓励大家多多使用它--前提是用好它。但问题就在于能不能用好它。

实现的方式固然百家争鸣,不过assert宏多数情况下和下面的定义相差不远:

gotcha28/myassert.h 
#ifndef NDEBUG 
#define assert(e) ((e) / 
    ? ((void)0) / 
    :__assert_failed(#e,__FILE__,__LINE__) ) 
#else 
#define assert(e) ((void)0) 
#endif
如果NDEBUG有定义,那么我们就没有在调试模式下,assert宏就会展开成一个空操作(no-op)。否则,我们就处在调试模式下,(在此特定实现中)assert宏就会展开成一个条件表达式以对某特定条件进行(谓词)测试。若该条件测试结果为false,则我们生成一条诊断信息并调用abort(以无条件强制终止程序运行)。

使用assert宏优于使用注释来文档化前置条件、后置条件及不变量(invariant)。一条assert宏,在生效时,会对执行特定条件来个运行时校验,所以不会被轻轻松松地被当作一个注释而被无视(参见常见错误1)。与注释不同的是,由于违反了assert宏的正确性校验的错误通常来说都被更正了,因为"调用abort"这种后果会使得"代码需要维护"这件事必须马上完成:

//gotcha28/myassert.cpp 
template
void doit( Cont &c, int index ) { 
    assert( index >= 0 && index < c.size() );       // #1 
    assert( process( c[index] ) );                  // #2 
    // ... 
}
在上面这段代码中,我们演示了几个使用assert宏的过程中犯下的用法错误。标了#2的那行代码是明显的误用,因为我们在调用一个函数,而这个函数被放到assert宏中去以后可能会有其副作用。这段代码的行为会随着NDEBUG符号有否被定义而有本质的不同。 这种assert宏的用法会导致在调试模式下代码行为完全正确,而把调试模式关掉后原有的软件缺陷就复现了。于是你会又打开调试模式,缺陷又消失了。然后你再关掉调试模式,结果……(死循环!)。

标了#1的那行代码错得更微妙。Cont class的成员函数size很有可能是一个常量成员函数,因此,它不会有副作用,对吗?错!除了size这个名字的习惯意义之外,我们找不到任何理由来保证该成员函数具有常量语义。就算它真的是常量成员函数,也不能保证对它的调用就(对代码的行为)没有副作用。(再退一步讲)就算(执行完size函数后)c的逻辑状态没有改变,它的物理状态仍然可能发生了变化(参见常见错误82)。最后,我们可不要忘了使用assert宏就是为了找出代码缺陷。即使调用size函数的本意并非要向代码行为中引入什么可觉察的(变化)效应,它的实现仍然可能包含缺陷(使得这种效应出现)。我们当然希望对assert宏的使用会将代码缺陷暴露于光天化日,而不是将它们藏匿起来。正确的assert宏的用法会避免其条件语句带有任何潜在的副作用:

template
void doit( Cont &c, int index ) { 
    const int size = c.size();      // 译注:
避免了size未被调用的潜在副作用 
    assert( index >= 0 && index < size );           // 正确 
    // ... 
}
显然,assert宏并非万金油,但它的确在位于注释和异常之间的某个位置扮演了代码文档化及捕捉非法行为的适当角色。其最大问题在于它到底是一个伪函数,因此它也(无可避免地)带着前面的条款中描述的有关伪函数的种种先天不足(参见常见错误26)。好在它是一个标准化了的伪函数,这也就暗示着其不足之处已为世人熟知。只要使用时多长个心眼,assert宏就能为我们造福。

原文:http://www.cnblogs.com/sunyubo/archive/2010/03/04/2282185.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值