do...while(0) do...while(false)的妙用

来源:互联网 发布:ngxopenresty 解析php 编辑:程序博客网 时间:2024/05/22 07:06

 

do...while(0) do...while(false)的妙用  
  在C++中,有三种类型的循环语句:for, while, 和do...while, 但是在一
般应用中作循环时, 我们可能用for和while要多一些,do...while相对不受重视。 
   但是,最近在读我们项目的代码时,却发现了do...while的一些十分聪明的用法,不是用来做循环,而是用作其他来提高代码的健壮性。 1. do...while(0)消除goto语句。 
通常,如果在一个函数中开始要分配一些资源,然后在中途执行过程中如果遇到错误则退出函数,当然,退出前先释放资源,我们的代码可能是这样: version 1 
[cpp] view plaincopy 
1. bool Execute()   2. {   
3.    // 分配资源   
4.    int *p = new int;   5.    bool bOk(true);   6.    
7.    // 执行并进行错误处理   8.    bOk = func1();   9.    if(!bOk)    10.   {   
11.      delete p;      12.      p = NULL;   13.      return false;   14.   }   15.   
16.   bOk = func2();   17.   if(!bOk)    18.   {   
19.      delete p;      20.      p = NULL;   21.      return false;   22.   }   23.   
24.   bOk = func3();  

 25.   if(!bOk)    
26.   {   
27.      delete p;      28.      p = NULL;   29.      return false;   30.   }   31.   
32.   // ..........   33.   
34.   // 执行成功,释放资源并返回   35.    delete p;      36.    p = NULL;   37.    return true;   38.      39.}   
这里一个最大的问题就是代码的冗余,而且我每增加一个操作,就需要做相应的错误处理,非常不灵活。于是我们想到了goto: version 2  
 
[cpp] view plaincopy 
1. bool Execute()   2. {   
3.    // 分配资源   
4.    int *p = new int;   5.    bool bOk(true);   6.    
7.    // 执行并进行错误处理   8.    bOk = func1();   
9.    if(!bOk) goto errorhandle;   10.   
11.   bOk = func2();   
12.   if(!bOk) goto errorhandle;   13.   
14.   bOk = func3();   
15.   if(!bOk) goto errorhandle;   16.   
17.   // ..........   18.   
19.   // 执行成功,释放资源并返回   20.    delete p;      21.    p = NULL;   22.    return true;   23.   

 

 


24.errorhandle:   25.    delete p;      26.    p = NULL;   27.    return false;   28.      29.}   
代码冗余是消除了,但是我们引入了C++中身份比较微妙的goto语句,虽然正确的使用 goto可以大大提高程序的灵活性与简洁性,但太灵活的东西往往是很危险的,它会让我们的程序捉摸不定,那么怎么才能避免使用goto语句,又能消除代码 冗余呢,请看do...while(0)循环: version3  
[cpp] view plaincopy 
1. bool Execute()   2. {   
3.    // 分配资源   
4.    int *p = new int;   5.    
6.    bool bOk(true);   7.    do   8.    {   
9.       // 执行并进行错误处理   10.      bOk = func1();   11.      if(!bOk) break;   12.   
13.      bOk = func2();   14.      if(!bOk) break;   15.   
16.      bOk = func3();   17.      if(!bOk) break;   18.   
19.      // ..........   20.   
21.   }while(0);   22.   
23.    // 释放资源   24.    delete p;      25.    p = NULL;   26.    return bOk;   27.      28.}   
 
“漂亮!”, 看代码就行了,啥都不用说了...  

 

 


2 宏定义中的do...while(0)   如果你是C++程序员,我有理由相信你用过,或者接触过,至少听说过MFC, 在MFC的afx.h文件里面, 你会发现很多宏定义都是用了do...while(0)或do...while(false), 比如说: 
#define AFXASSUME(cond)       do { bool __afx_condVal=!!(cond); 
ASSERT(__afx_condVal); __analysis_assume(__afx_condVal); } while(0) 粗看我们就会觉得很奇怪,既然循环里面只执行了一次,我要这个看似多余的do...while(0)有什么意义呢?  当然有! 
为了看起来更清晰,这里用一个简单点的宏来演示: 
#define SAFE_DELETE(p) do{ delete p; p = NULL} while(0) 假设这里去掉do...while(0), 
#define SAFE_DELETE(p) delete p; p = NULL; 那么以下代码: 
if(NULL != p) SAFE_DELETE(p) else   ...do sth... 就有两个问题, 
1) 因为if分支后有两个语句,else分支没有对应的if,编译失败 2) 假设没有else, SAFE_DELETE中的第二个语句无论if测试是否通过,会永远执行。 
你可能发现,为了避免这两个问题,我不一定要用这个令人费解的do...while,  我直接用{}括起来就可以了 
#define SAFE_DELETE(p) { delete p; p = NULL;} 
的确,这样的话上面的问题是不存在了,但是我想对于C++程序员来讲,在每个语句后面加分号是一种约定俗成的习惯,这样的话,以下代码: if(NULL != p) SAFE_DELETE(p); else   ...do sth... 
其else分支就无法通过编译了(原因同上),所以采用do...while(0)是做好的选择了。 
也许你会说,我们代码的习惯是在每个判断后面加上{}, 就不会有这种问题了,也就不需要do...while了,如: if(...)  { } else { } 
诚然,这是一个好的,应该提倡的编程习惯,但一般这样的宏都是作为library的一部分出现的,而对于一个library的作者,他所要做的就是让其库具有通用性,强壮性,因此他不能有任何对库的使用者的假设,如其编码规范,技术水平等。 
3 消除多层嵌套的条件语句 

 

 


  举个例子:你去商店买裙子,进入商店后发现只有一条(假设哈)。你想要红
色的连衣裙。有个Check函数,以商店的裙子作为参数,检查它是否符合你的要求。符合则付钱,然后离开,不符合直接离开。   那么正常情况的代码如下: [cpp] view plaincopy 
1. bool   Check( item )   2. {   
3.    if(  是连衣裙 )   4.   {   
5.     if(是红色)   6.     {   
7.       if(合身)   8.                  {   
9.                        付钱   10.                 }   11.    }   12.  }   
13.  离开商店   14.}   
  当条件不适合的时候,就要跳过付钱环节,但离开商店是必须得做的。是不是觉得这个if的嵌套太复杂呢?如果还不明显,你可以在条件中再加入价钱等其他更多的条件,这样,这个深层次的if嵌套就很不美观了。(换用否定条件也不能改变if的嵌套) 
  而如果使用do-while(false),则可去除这种嵌套。代码变成如下: [cpp] view plaincopy 
1. void  Check( item )   2. {   
3.   do   4.      {   
5.           if( 不是连衣裙)   6.       break;   
7.           if( 不是红色 )   8.       break;   9.     if( 不合身 )   10.                break;   11.         付钱   
12.     }while(false);   13.   

 

 


 

原创粉丝点击