在C++中,do...while 通常是用来做循环用的,然而我们做循环操作可能用for和while要多一些。经常看到一些开源代码会出现do...while(0)这样的代码,这样的代码看上去肯定不是用来做循环的,那为什么要这样用呢?
实际上do...while(0)的作用远大于美化代码,现总结起来主要有以下几个作用:
辅助定义复杂的宏,避免引用的时候出错,提高代码健壮性
假设你需要定义一个这样的宏:
1. #define DOSOMETHING()\ 2. 3. func1();\ 4. 5. func2();
这个宏的本意是,当调用DOSOMETHING()时,函数func1()和func2()都会被调用。但是如果你在调用的时候这么写:
1. if(a>0) 2. DOSOMETHING();
因为宏在预处理的时候会直接被展开,你实际上写的代码是这个样子的:
1. if(a>0) 2. 3. func1(); 4. 5. func2();
这就出现了问题,因为无论a是否大于0,func2()都会被执行,导致程序出错。
那么仅仅使用{}将func1()和func2()包起来行么?
我们在写代码的时候都习惯在语句右面加上分号,如果在宏中使用{},代码里就相当于这样写了:“{...};”,假如有以下代码:
1. #define DOSOMETHING(){\ 2. 3. func1();\ 4. 5. func2();} 6. ... 7. if(a>0) 8. DOSOMETHING(); 9. else 10. ... 11. ...
展开后就是这个样子:
1. if(a>0) 2. { 3. func1(); 4. 5. func2(); 6. }; 7. else 8. ...
这样是不会编译通过。所以,很多人才采用了do{...}while(0);
1. #define DOSOMETHING() \ 2. 3. do{ \ 4. 5. func1();\ 6. 7. func2();\ 8. 9. }while(0)\ 10. 11. ... 12. if(a>0) 13. DOSOMETHING(); 14. else 15. ... 16. ...
消除分支语句或者goto语句,提高代码的易读性
如果在一个函数中开始要分配一些资源,然后在中途执行过程中如果遇到错误则退出函数,当然,退出前先释放资源,我们的代码可能是这样:
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:
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)循环:
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. }
使用代码块,代码块内定义变量,不用考虑变量重复问题
当你的功能很复杂,变量很多你又不愿意增加一个函数的时候,使用do{}while(0);,将你的代码写在里面,里面可以定义变量而不用考虑变量名会同函数之前或者之后的重复。