do...while(0)的妙用

2023-11-01

    在C++中,有三种类型的循环语句:for, while, 和do...while, 但是在一般应用中作循环时, 我们可能用for和while要多一些,do...while相对不受重视。
    但是,最近在读我们项目的代码时,却发现了do...while的一些十分聪明的用法,不是用来做循环,而是用作其他来提高代码的健壮性。

1. do...while(0)消除goto语句。
通常,如果在一个函数中开始要分配一些资源,然后在中途执行过程中如果遇到错误则退出函数,当然,退出前先释放资源,我们的代码可能是这样:
version 1

bool  Execute()
{
   
//  分配资源
    int   * =   new   int ;
   
bool  bOk( true );

   
//  执行并进行错误处理
   bOk  =  func1();
   
if ( ! bOk) 
   {
      delete p;   
      p 
=  NULL;
      
return   false ;
   }

   bOk 
=  func2();
   
if ( ! bOk) 
   {
      delete p;   
      p 
=  NULL;
      
return   false ;
   }

   bOk 
=  func3();
   
if ( ! bOk) 
   {
      delete p;   
      p 
=  NULL;
      
return   false ;
   }

   
//  ..........

   
//  执行成功,释放资源并返回
    delete p;   
    p 
=  NULL;
    
return   true ;
   
}


这里一个最大的问题就是代码的冗余,而且我每增加一个操作,就需要做相应的错误处理,非常不灵活。于是我们想到了goto:
version 2

bool  Execute()
{
   
//  分配资源
    int   * =   new   int ;
   
bool  bOk( true );

   
//  执行并进行错误处理
   bOk  =  func1();
   
if ( ! bOk)  goto  errorhandle;

   bOk 
=  func2();
   
if ( ! bOk)  goto  errorhandle;

   bOk 
=  func3();
   
if ( ! bOk)  goto  errorhandle;

   
//  ..........

   
//  执行成功,释放资源并返回
    delete p;   
    p 
=  NULL;
    
return   true ;

errorhandle:
    delete p;   
    p 
=  NULL;
    
return   false ;
   
}


代码冗余是消除了,但是我们引入了C++中身份比较微妙的goto语句,虽然正确的使用goto可以大大提高程序的灵活性与简洁性,但太灵活的东西往往是很危险的,它会让我们的程序捉摸不定,那么怎么才能避免使用goto语句,又能消除代码冗余呢,请看do...while(0)循环:
version3

bool  Execute()
{
   
//  分配资源
    int   * =   new   int ;

   
bool  bOk( true );
   
do
   {
      
//  执行并进行错误处理
      bOk  =  func1();
      
if ( ! bOk)  break ;

      bOk 
=  func2();
      
if ( ! bOk)  break ;

      bOk 
=  func3();
      
if ( ! bOk)  break ;

      
//  ..........

   }
while ( 0 );

    
//  释放资源
    delete p;   
    p 
=  NULL;
    
return  bOk;
   
}


“漂亮!”, 看代码就行了,啥都不用说了...

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的作者,他所要做的就是让其库具有通用性,强壮性,因此他不能有任何对库的使用者的假设,如其编码规范,技术水平等。 

 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

do...while(0)的妙用 的相关文章

  • 通过 SocketCAN 进行 boost::asio

    我正在考虑利用升压阿西奥 http www boost org doc libs 1 49 0 doc html boost asio html从a读取数据套接字CAN http en wikipedia org wiki SocketCA
  • 如何在 DataColumn.Expression 中使用 IF/ELSE 或 CASE?

    我有一个包含 1 列的表 状态 我想添加另一列名为 Action 的列 其值如下 如果 Status Yes 则 Action Go 否则 Action Stop 我使用以下代码添加到 操作 列中 但它不起作用 myDataTable Co
  • 如何保证对象只有一个线程

    我有以下代码 class Service public void start creates thread which creates window and goes to message loop void stop sends WM C
  • 我如何理解这个 C 类型声明?

    double bar int double double double double 在查看讲座幻灯片时 我发现了留给学生的练习 用简单的英语来说 什么是类型bar在这个 C 声明中 Please帮助我解决这个问题 我什至不知道从哪里开始
  • 使用 Enumerable.OfType() 或 LINQ 查找特定类型的所有子控件

    Existed MyControl1 Controls OfType
  • 在 Xamarin 中隐藏软键盘

    如何隐藏软键盘以便在聚焦时显示Entry在 Xamarin forms 便携式表单项目中 我假设我们必须为此编写特定于平台的渲染器 但以下内容不起作用 我创建自己的条目子类 public class MyExtendedEntry Entr
  • 防止 boost::asio::io_context 在空轮询调用时停止

    此代码调用发布的句柄 boost asio io context ioc boost asio post ioc std cout lt lt lol lt lt std endl ioc poll 而这并没有 boost asio io
  • ASP.Net Core 内容配置附件/内联

    我正在从 WebAPI 控制器返回一个文件 Content Disposition 标头值自动设置为 附件 例如 处置 附件 文件名 30956 pdf 文件名 UTF 8 30956 pdf 当它设置为附件时 浏览器将要求保存文件而不是打
  • fprintf() 线程安全吗?

    我正在为野人就餐问题的某些变量编写一个 C 解决方案 现在 我创建线程 每个线程都将 FILE 获取到同一个调试文件 在线程内我正在使用 fprintf 进行一些打印 打印的语句不受任何类型的互斥锁等保护 我没有在调试文件中观察到任何交错行
  • 类的成员复制

    在学习 复制成员 概念时 书中给出了如下说法 此外 如果非静态成员是引用 const 或没有复制赋值的用户定义类型 则无法生成默认赋值 我不太明白这个声明到底想传达什么 或者说这个说法指的是哪一种场景 谢谢 该语句与编译器自动为您编写的类
  • C# 构建一个 webservice 方法,它接受 POST 方法,如 HttpWebRequest 方法

    我需要一个接受 POST 方法的 Web 服务 访问我的服务器正在使用 POST 方法 它向我发送了一个 xml 我应该用一些 xml 进行响应 另一方面 当我访问他时 我已经使用 HttpWebRequest 类进行了管理 并且工作正常
  • 如何在标准 WPF ListView 中启用 UI 虚拟化

    我正在使用 NET 4 5 VS2012 并且我有一个 ListView 看起来像这样
  • C# 中的 strstr() 等效项

    我有两个byte 我想找到第二个的第一次出现byte 在第一个byte 或其中的一个范围 我不想使用字符串来提高效率 翻译第一个byte to a string会效率低下 基本上我相信就是这样strstr 在 C 中做 最好的方法是什么 这
  • 原则 2 OneToMany 级联 SET NULL

    错误 无法删除或更新父行 外键约束失败 课程 class Teacher ORM OneToMany targetEntity publication mappedBy teacher protected publications clas
  • 新任务中使用的依赖注入服务

    我在需要时使用依赖项注入来访问我的服务 但我现在想要创建一个并发任务 但这会由于依赖项注入对象及其生命周期而导致问题 我读过这篇文章 标题 防止多线程 Link http mehdi me ambient dbcontext in ef6
  • 跨多个域的 ASP.NET 会话

    是否有合适的 NET 解决方案来在多个域上提供持久服务器会话 即 如果该网站的用户在 www site1 com 下登录 他们也将在 www site2 com 下登录 安全是我们正在开发的程序的一个问题 Thanks 它是否需要在会话中
  • 使用taskkill停止Windows服务

    我需要帮助来使用 C 终止 Windows 服务 现在要终止该服务 请使用以下选项 从命令 sc queryex ServiceName 发现后PID服务的 taskkill pid 1234 exemple f 为了便于阅读 但如果您明白
  • 更改 Windows Phone 系统托盘颜色

    有没有办法将 Windows Phone 上的系统托盘颜色从黑色更改为白色 我的应用程序有白色背景 所以我希望系统托盘也是白色的 您可以在页面 XAML 中执行此操作
  • 在简单注入器中解析具有自定义参数的类

    我正在使用以下命令创建 WPF MVVM 应用程序简易注射器作为 DI 容器 现在 当我尝试从简单注入器解析视图时遇到一些问题 因为我需要在构造时将参数传递到构造函数中 而不是在将视图注册到容器时 因此这不是适用的 简单注入器将值传递到构造
  • 使我的 COM 程序集调用异步

    我刚刚 赢得 了在当前工作中维护用 C 编码的遗留库的特权 这个dll 公开使用 Uniface 构建的大型遗留系统的方法 除了调用 COM 对象之外别无选择 充当此遗留系统与另一个系统的 API 之间的链接 在某些情况下 使用 WinFo

随机推荐

  • 【图像分割】实现snake模型的活动轮廓模型以进行图像分割研究(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 图像分割是计算机视觉领域的一个重要研究方向
  • element中el-table根据状态改变样式,你不能错过的技能

    前言 在开发中 如何根据数据的状态来改变表格的样式一直是一个常见的难题 如果你也曾经为此苦恼过 那么本文将为你介绍如何使用 element 中的 el table 来实现这一目标 让你的数据展示更加直观 易于理解 需求效果 需求 表格中当前
  • 筛选kepler.csv中未下载到的kepid

    In 1 判断输入字符串是否在路径中函数 import os def findfile2 keyword keyword 9d int keyword judgeExist False root os path join C Users 9
  • snapgene设计引物_分子克隆之引物设计(一)

    分子克隆之引物设计 一 目的 将目的基因Homosapiens interleukin 37利用引物扩增表达带上限制酶酶切位点 再与载体pcDNA3 1 3xFlag C酶切连接 师兄给定的酶切位点是 固定 BamH I GGATCC Xh
  • bilibili直播间接收监听和发送弹幕api

    参考文 https blog csdn net fei347795790 article details 111164254 Python实现自动发送B站直播弹幕软件 https blog csdn net fzq13994952987 a
  • 双目相机计算稠密深度点云详解教程

    点击上方 计算机视觉工坊 选择 星标 干货第一时间送达 作者 本文为3D视觉开发者社区特约作者 熊猫飞天授权发布 引言 本教程主要内容为介绍如何使用双目相机计算出稠密的3D点云 主要涉及到elas包的使用 通过KITTI数据集和ZED相机进
  • node生成唯一设备id(node-machine-id)

    npm安装 npm install node machine id yarn安装 yarn add node machine id 有 异步 同步 两种生成方式 import machineId machineIdSync from nod
  • MYSQL

    regexp extract regexp extract str regexp idx str是被解析的字符串或字段名 regexp 是正则表达式 idx是返回结果 取表达式的哪一部分 默认值为1 0表示把整个正则表达式对应的结果全部返回
  • Synchronized锁对象详解

    synchronized 是我们的同步的一种选择 加锁就要有对应的钥匙 这个钥匙分为 当前类对象 当前class对象 第三方对象 synchronized 可以修饰 变量 方法 我们以方法举例可以有如下的几种情况 修饰静态方法 这里默认的锁
  • Netty和Tomcat区别

    Netty与Tomcat区别 它们的区别不少 最大的区别就在于通信协议 这是众所周知的 Tomcat是一个服务器 它一定是基于Http协议的 它的实质是一个基于http协议的web容器 Netty则不同 Netty可以通过编程自定义各种协议
  • docker学习:dockerfile介绍

    这里主要是介绍dockerfile dockerfile就是用来构建docker镜像的脚本 脚本就意味着会有相关的dockerfile命令 也是我们要学习的一部分 构建步骤 1 编写一个Dockerfile文件 2 docker build
  • win10网络诊断为DNS服务器未响应,Windows10网络诊断DNS服务器未响应的解决办法

    Windows10网络诊断DNS服务器未响应怎么办 不少win10系统用户在遇到网络无法连接的时候 诊断网络会提示Windows10网络诊断DNS服务器未响应 那么这个问题要如何解决呢 下面小编为大家带来详细的解决办法 Windows10网
  • 机器学习的概率统计模型(附代码)(一)

    目录 概率论 1 1 离散随机变量分布 1 伯努利分布 2 二项分布 3 泊松分布 1 2 连续随机变量分布 1 均匀分布 2 指数分布 3 正态分布 总结 系列文章目录 概率论 概率论 是研究随机现象数量规律的数学分支 随机现象是相对于决
  • openai Python库

    OpenAI 是一个人工智能研究机构 他们开发了一个 Python 库 用于帮助开发者使用人工智能技术 这个库包含了许多用于自然语言处理 机器学习 深度学习和其他人工智能应用的工具和算法 您可以在 OpenAI 的 GitHub 页面上找到
  • Mysql连接问题

    连接MySQL长时间不连接后报错 com mysql cj core exceptions ConnectionIsClosedException No operations allowed after connection closed
  • eNSP华为模拟器使用——(11)eNSP模拟无线AC和AP

    eNSP模拟无线AC和AP 1 拓扑 2 需求 实现AC和AP二层关联 3 配置 dhcp enable interface Vlanif 1 ip address 192 168 10 1 255 255 255 0 dhcp selec
  • MySql 字符串数据类型

    字符串类型 CHAR CHAR类型是定长的 会根据定义的长度分配空间 当存储CHAR值时 mysql会删除所有的末尾空格 CHAR很适合存储很短的字符串 或者所有的值都接近同一个长度 例如 CHAR非常适合存储密码的MD5的值 因为这是一个
  • Java毕设 SSM中药店商城系统(含源码+论文)

    文章目录 1 项目简介 2 实现效果 2 1 界面展示 3 设计方案 3 1 概述 3 2 系统流程 3 3 系统结构设计 4 项目获取 1 项目简介 Hi 各位同学好呀 这里是M学姐 今天向大家分享一个今年 2022 最新完成的毕业设计项
  • id 重启event_系统已在未先正常关机的情况下重新启动 事件ID:41

    创建日期 2018 07 18 系统已在未先正常关机的情况下重新启动 事件ID 41 日志名称 System 来源 Microsoft Windows Kernel Power 日期 2018 7 17 23 55 06 事件 ID 41
  • do...while(0)的妙用

    在C 中 有三种类型的循环语句 for while 和do while 但是在一般应用中作循环时 我们可能用for和while要多一些 do while相对不受重视 但是 最近在读我们项目的代码时 却发现了do while的一些十分聪明的用