C++中do...while(false)妙用

本文介绍了C++中do...while(false)循环的特殊用法,主要用于宏函数定义和非循环结构的代码优化。在宏函数中,do...while(false)能确保代码块被视为单个语句,避免了if-else匹配错误。在非循环结构中,利用do...while(false)可以消除冗余的错误处理代码,提高代码的整洁性和可读性。通过示例代码展示了如何使用这种方法来简化内存管理及错误处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++中do…while(false)妙用

C++中,循环逻辑有三种格式:forwhile以及do...while();,前两种是比较常用的,循环逻辑也很直观,但是do...while()循环一般比较少见,它其实更多地是用在宏函数定义和代码结构优化中,具体看下面代码:

用在宏函数中

加入需要定义一个释放内存的宏函数,如下:

#define SAFE_DELETE(ptr)                                                                                               \
  delete ptr;                                                                                                          \
  ptr = nullptr;

使用场景如下:

void foo() {
  int *ptr = new int(0);
  if (ptr != nullptr)
    SAFE_DELETE(ptr);
  else {
    /* do something */
  }
  return;
}

此时,宏函数展开后的代码实际上这样的:

void foo() {
  int *ptr = new int(0);
  if (ptr != nullptr)
    delete ptr;
  ptr = nullptr;
  else {
    /* do something */
  }
  return;
}

两个问题:

  • 由于释放函数含有两条语句,只有一条语句会被if语句嵌入,从而导致else分支无法匹配到相对应的if语句,编译错误
  • 代码ptr = nullptr,不论if语句成立与否,都会被执行,偏离此代码初衷

解决办法:

在宏函数定义处,或者宏函数使用处用{}限制作用域,仍然存在两个问题:

  • 宏函数定义处,用{}包裹宏函数,那么在使用该函数且存在else分支时,宏函数使用处末尾不能加;,否则else分支仍然编译错误,但是在C++中,语句末尾加;是一种习惯
  • 在对应的if分支后,将宏函数用{}包裹,这样就避免了上述问题,而且也是提倡的编程习惯,但是这种宏函数更多的是用在库的代码中,库的的开发者不能依靠使用库的程序员有良好的编程习惯来保证宏函数的正确使用,就没有一种普适的方法么?有的,看以下代码
#define SAFE_DELETE(ptr)                                                                                               \
  do {                                                                                                                 \
    delete ptr;                                                                                                        \
    ptr = nullptr;                                                                                                     \
  } while (false)

此时,我们用do...while(false)循环将宏函数包裹,因为循环条件恒为假,循环体只会执行一次,且被包裹的循环体是被当做一条语句体来处理的,因此不会有上面所述的错误发生,在各种源码框架中,这样的用法极其普遍,算是一种奇技淫巧吧。再看第二种用法,这个也是最近在看公司项目代码时产生的疑问,所以在网上查了下,自己也总结了下。

用于非循环结构

这种用法主要是为了消除冗余代码,简化代码结构,考虑以下代码,因为在函数开始动态申请了内存,而这个内存作用域仅在函数体内,因此函数退出时需要释放这片内存,防止内存泄漏。如下代码所示,我们可以用条件语句实现这个逻辑:

bool func1();
bool func2();
bool func3();

void goo1() {
  int *ptr = new int(0); // 函数退出时需要释放该内存

  if (!func1()) {
    delete ptr;
    ptr = nullptr;
    return;
  }
  if (!func2()) {
    delete ptr;
    ptr = nullptr;
    return;
  }
  if (!func3()) {
    delete ptr;
    ptr = nullptr;
    return;
  }

  delete ptr;
  ptr = nullptr;
  return;
}

当函数执行过程中发生错误退出时,因为需要释放掉前面申请的内存,所以每次执行失败时都要去清理内存,但是代码看起来却很丑陋,很冗余,同样的代码写了四遍,C++兼容C,自然也可以使用goto语句来整理代码,如下:

void goo2() {
  int *ptr = new int(0); // 函数退出时需要释放该内存

  if (!func1())
    goto release_memory;
  if (!func2())
    goto release_memory;
  if (!func3())
    goto release_memory;

release_memory:
  delete ptr;
  ptr = nullptr;
  return;
}

将末尾的释放代码打上标签,每当函数发生错误便无条件跳转到释放内存的代码处开始执行,不失为一种很优雅的方法,但是在C++中,使用goto语句本身就是一种很敏感的行为,虽然像这样在函数体内使用是没有风险的,而且在一些库中,这样的写法也随处可见,当然了,我们也可以封装一个函数或者lambda函数去处理也是可以的,但是像这种局部性比较强的代码没必有封装一个函数,那有没有一种更为优雅的实现方式呢?看以下代码:

void goo3() {
  int *ptr = new int(0); // 函数退出时需要释放该内存

  do {
    if (!func1())
      break;
    if (!func2())
      break;
    if (!func3())
      break;
  } while (false);

  delete ptr;
  ptr = nullptr;
  return;
}

但凡函数执行过程中发生错误,循环体会立即break,函数会执行内存释放处的逻辑,对比第一个版本,不仅消除了冗余代码,而且代码结构也很清晰。当然,如果函数发生错误时,不需要做什么清理工作,也可以直接return出去的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值