当代码流是这样的:

if(check())
{
  ...
  ...
  if(check())
  {
    ...
    ...
    if(check())
    {
      ...
      ...
    }
  }
}

我通常看到这种方法可以避免上述混乱的代码流:

do {
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
} while(0);

有什么更好的方法可以避免这种工作/黑客,从而使其成为更高级别(行业级别)的代码?

任何跳出盒子的建议都是欢迎的!


当前回答

如何移动if语句到一个额外的函数,产生一个数值或enum结果?

int ConditionCode (void) {
   if (condition1)
      return 1;
   if (condition2)
      return 2;
   ...
   return 0;
}


void MyFunc (void) {
   switch (ConditionCode ()) {
      case 1:
         ...
         break;

      case 2:
         ...
         break;

      ...

      default:
         ...
         break;
   }
}

其他回答

首先,一个简短的例子来说明为什么goto不是一个好的c++解决方案:

struct Bar {
    Bar();
};

extern bool check();

void foo()
{
    if (!check())
       goto out;

    Bar x;

    out:
}

试着把它编译成一个object文件,看看会发生什么。然后尝试等价的do+ break + while(0)。

这是题外话。主要观点如下。

如果整个函数失败,这些小块的代码通常需要某种类型的清理。当您“展开”部分完成的计算时,这些清理通常希望以与块本身相反的顺序发生。

获得这些语义的一个选项是RAII;参见@utnapistim的回答。c++保证自动析构函数以与构造函数相反的顺序运行,这自然提供了一个“unwind”。

但这需要大量的RAII类。有时一个更简单的选择是使用堆栈:

bool calc1()
{
    if (!check())
        return false;

    // ... Do stuff1 here ...

    if (!calc2()) {
        // ... Undo stuff1 here ...
        return false;
    }

    return true;
}

bool calc2()
{
    if (!check())
        return false;

    // ... Do stuff2 here ...

    if (!calc3()) {
        // ... Undo stuff2 here ...
        return false;
    }

    return true;
}

...等等。这很容易审计,因为它把“撤销”代码放在“做”代码旁边。简单的审计是好的。它还使控制流非常清晰。对于C来说,这也是一个有用的模式。

它可能要求calc函数接受大量参数,但如果您的类/结构具有良好的内聚性,这通常不是问题。(也就是说,属于一起的东西生活在一个对象中,所以这些函数可以作为指向少量对象的指针或引用,但仍然可以做很多有用的工作。)

Try to extract the code into a separate function (or perhaps more than one). Then return from the function if the check fails. If it's too tightly coupled with the surrounding code to do that, and you can't find a way to reduce the coupling, look at the code after this block. Presumably, it cleans up some resources used by the function. Try to manage these resources using an RAII object; then replace each dodgy break with return (or throw, if that's more appropriate) and let the object's destructor clean up for you. If the program flow is (necessarily) so squiggly that you really need a goto, then use that rather than giving it a weird disguise. If you have coding rules that blindly forbid goto, and you really can't simplify the program flow, then you'll probably have to disguise it with your do hack.

我不是c++程序员,所以我不会在这里写任何代码,但到目前为止还没有人提到面向对象的解决方案。下面是我的猜测:

拥有一个通用接口,该接口提供了一个方法来评估单个条件。现在,您可以在包含有问题的方法的对象中使用这些条件的实现列表。遍历列表并计算每个条件,如果其中一个条件失败,可能会提前爆发。

这样的设计很好地遵循了开/闭原则,因为在初始化包含相关方法的对象时,可以很容易地添加新的条件。您甚至可以向接口添加第二个方法,该方法用于条件评估,返回条件的描述。这可以用于自文档系统。

但是,缺点是由于使用了更多的对象和遍历列表,所涉及的开销稍微多一些。

如果你不需要在执行过程中引入局部变量,那么你通常可以将其平化:

if (check()) {
  doStuff();
}  
if (stillOk()) {
  doMoreStuff();
}
if (amIStillReallyOk()) {
  doEvenMore();
}

// edit 
doThingsAtEndAndReportErrorStatus()

将其合并为一个if语句:

if(
    condition
    && other_condition
    && another_condition
    && yet_another_condition
    && ...
) {
        if (final_cond){
            //Do stuff
        } else {
            //Do other stuff
        }
}

这是在Java等语言中使用的模式,其中删除了goto关键字。