C++11之断言(影响面广)

很可笑,我很少用断言,对断言的不了解程度与初学者无异。今天翻看了《深入理解C++11》,新标准中对断言做了改进,至少说明断言还是很有用的。结合《代码大全》、《C++应用程序性能优化》把自己对断言的理解写下来以方便自己日后用到时查阅。

一、为什么要用断言

首先要搞清楚为什么要用断言,不能看别人代码中有,就追赶时髦地用一用!从效果上来说assert断言能用if语句替换,那么为什么不用if语句把断言替换呢?一般而言,if语句是处理逻辑上的可能会发生的错误,断言则用来处理不应该发生的状况。

什么是不应该发的的状况呢?这要区分数据的来源:1、数据来源于系统内部(子程序、子模块间的调用)2、数据来源于系统外部(外部设备如键盘的输入、串口数据的读取、网络数据的读取)。对内部来源的数据,我们没法去通过常规的测试手段去验证,此时断言就用上了。

  当然你如果硬是要用if语句也没人说你不对,但大量的if语句出现在源码中时,会造成代码臃肿,降低了可读性,另外会产生不紧凑代码,影响效率。

程序开发初期,码农们忽视的是程序间调用参数的合法性,对这些参数可使用断言来防止意外,随着程序进入release版时,可以定义NDEBUG来让断言失效。以下是NDEGBU对assert的处理代码。


#ifdef NDEBUG

#define assert(expr) (static_cast<void> (0))

#else

......

#endif


二、如何使用断言

assert是一个宏,C语言原型定义在assert.h中,C++语言原型定义在cassert中,形式为:void assert(int expression);参数为表达式,如果为0则向stderr打印一条出错信息,再调用 abort来终止程序。如果为真,则继续执行其后的语句。

以下以伪代码方式讲解assert使用方式及注意事项:

// 从堆中动态申请内存

char * newBuff(int nSize/*申请内存大小*/)

 {   

assert(nSize> 0);  

assert(nSize <= MAX_BUFFER_SIZE);

return (new char[nSize] );

int main()

{

char *pCh  = newBuff(0);

......

        delete pCh; 

pCh = NULL;

        ......

}

说明:assert在子函数开始处对参数进行验证,验证条件分开,这样出现问题可以准确定位错误,如写成:assert(nSize>0 && nSzie<=MAX_BUFFER_SIZE);这样就不好了。

assert中不能有改变变量状态的操作,如:assert(iCount++ >= 0); 

需要注意的是:assert是宏,编译时会在使用处进行代码展开,如果程序中大量且频繁使用,会造成代码臃肿,影响性能。


三、C++11中关于断言的表述

上面例子中的断言只能在程序运行时起作用。即是动态断言。有时,程序员更希望在编译期间就能使用断言,这类断言称为静态断言,C++11提供了支持。


3.1、动态断言示例

enum FeatureSupports{

C99 0x0001,

ExtInt 0x0004,

SAssert=0x0008,

NoExcept0x0010,

};

struct Compiler{

const char * name;

int spp;  // 使用FeatureSuppport枚举

};

int main()

{

assert((SMAX-1) == (C99 | ExtInt | SAssert | NoExcept ));

Compiler a = {"abc", (C99 | SAssert)};

......

if( a.spp & C99)

......

}

以上是常见的C代码,功能是按位存储属性,枚举类中列出了编译器支持的各种属性,Compiler类成员变量spp,则是用来对属性的选择而设置的。主程序中就使用assert对所有的枚举量进行校验。采用的方法很巧妙,利用了位或的特点来验证,只要有一个类型写错或漏写,通过断言都能发现!遗憾的是上述程序必须运行后才能发现问题!

C++关于模板的实现中也可以举出相应的例子:

template <class T, class U> 

int bit_copy(T& a, U& b){

assert(sizeof(a) == sizeof(b));

memcpy(&a, &b, sizeof(b));

}

int main()

{

int a = 0x1234;

double b= 0.11;

bit_copy(a, b);

return 1;

}


3.2、静态断言示例

结合assert_static宏定义和“除0”出错,C++98实现静态断言。

#define assert_static(e) \

do{ \

enum{ assert_static__ = 1/(e)}; \

}while(0)

template <class T, class U> 

int bit_copy(T& a, U& b){

assert_static(sizeof(a) == sizeof(b));

memcpy(&a, &b, sizeof(b));

}

int main()

{

int a = 0x1234;

double b= 0.11;

bit_copy(a, b);

return 1;

}

C++11则提供了static_assert实现了静态断言,上面的例子可以改为:

template <class T, class U> 

int bit_copy(T& a, U& b){

static_assert(sizeof(a) == sizeof(b), "the parameters of bit_copy should have same width");

memcpy(&a, &b, sizeof(b));

}

int main()

{

int a = 0x1234;

double b= 0.11;

bit_copy(a, b);

return 1;

}

注意:C++98中定义的是assert_static,C++11中则为static_assert,正好对调!此时C++11编译要使用-std=c++11开关项。
static_assert是编译期间进行断言,使用范围比assert大,可以用于任何名字空间。如下述代码:
static_assert(sizeof(int)==4, "This is 32-bit Machine!";
int main(){return 0}
正由于是编译期间断言,所以static_assert表达式中不能有变量,如下则为错误代码:
int positive(const int a){
static_assert(a>0, "value must > 0");
}
此时要求助于assert来完成检验!

四、关于断言的使用原则

最后分享一下《代码大全》中对断言使用的建议:

1、用错误处理代码处理预期会发生的状况,用断言来处理绝不应该发生的状况;

2、避免把需要执行的代码放到断言中;

3、用断言来注解并验证前条件和后条件;

4、对于高健壮性的代码,应该先使用断言再处理错误 ;


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值