假设我有这样的伪代码:

bool conditionA = executeStepA();
if (conditionA){
    bool conditionB = executeStepB();
    if (conditionB){
        bool conditionC = executeStepC();
        if (conditionC){
            ...
        }
    }
}

executeThisFunctionInAnyCase();

函数executeStepX当且仅当前一个成功时执行。 在任何情况下,executeThisFunctionInAnyCase函数都应该在最后被调用。 我在编程方面是一个新手,所以很抱歉提出一个非常基本的问题:有没有一种方法(例如在C/ c++中)以代码易读性为代价,避免长if链产生那种“金字塔式代码”?

我知道如果我们可以跳过executeThisFunctionInAnyCase函数调用,代码可以简化为:

bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;

但是约束是executeThisFunctionInAnyCase函数调用。 break语句可以以某种方式使用吗?


当前回答

关于当前的代码示例,本质上是第二个问题,

[...block of code...]
bool conditionA = executeStepA();    
if (conditionA){
    [...block of code...]
    bool conditionB = executeStepB();
    if (conditionB){
        [...block of code...]
        bool conditionC = executeStepC();
        if (conditionC){
            ...other checks again...
        }
    }
}

executeThisFunctionInAnyCase();

除了将函数结果存储在变量中之外,这是典型的C代码。

如果布尔函数导致信号失败,那么c++的方法是使用异常,并将其编码为

struct Finals{ ~Finals() { executeThisFunctionInAnyCase(); } };

Finals finals;
// [...block of code...]
executeStepA();
// [...block of code...]
executeStepB();
// [...block of code...]
executeStepC();
//...other checks again...

然而,根据实际问题的不同,细节可能会有很大差异。

当我需要这样的通用最终操作时,我通常使用通用范围保护类,而不是当场定义一个自定义结构。作用域保护是由Petru Marginean为c++ 98发明的,然后使用临时生命周期扩展技巧。在c++ 11中,一般的范围保护类可以基于提供lambda表达式的客户端代码简单地实现。

在问题的最后,你建议了一个很好的C方法来做到这一点,即使用break语句:

for( ;; ) // As a block one can 'break' out of.
{
    // [...block of code...]
    if( !executeStepA() ) { break; }
    // [...block of code...]
    if( !executeStepB() ) { break; }
    // [...block of code...]
    if( !executeStepC() ) { break; }
    //...other checks again...
    break;
}
executeThisFunctionInAnyCase();

或者,对于C,将代码块中的代码重构为一个单独的函数,并使用return而不是break。因为它支持嵌套循环或开关,所以这更清楚也更通用。然而,你问的是休息。

与基于异常的c++方法相比,这种方法依赖于程序员记得检查每个函数的结果,并做正确的事情,这两者在c++中都是自动化的。

其他回答

代码中的IF/ELSE链不是语言的问题,而是程序设计的问题。如果你能够重构或重写你的程序,我建议你在Design Patterns (http://sourcemaking.com/design_patterns)中寻找更好的解决方案。

通常,当您在代码中看到大量IF & else时,就有机会实现策略设计模式(http://sourcemaking.com/design_patterns/strategy/c-sharp-dot-net)或其他模式的组合。

我确信有替代方案来编写一长串if/else,但我怀疑它们除了对你来说看起来很漂亮之外不会改变任何东西(然而,情人眼里出佳人仍然适用于代码:-))。你应该关心这样的事情(6个月后,当我有一个新的条件,我不记得关于这个代码的任何事情,我是否能够轻松地添加它?)或者如果链发生变化,我将如何快速和无错误地实现它)

假设你不需要单独的条件变量,反转测试并使用else-falthrough作为“ok”路径,将允许你获得更垂直的if/else语句集:

bool failed = false;

// keep going if we don't fail
if (failed = !executeStepA())      {}
else if (failed = !executeStepB()) {}
else if (failed = !executeStepC()) {}
else if (failed = !executeStepD()) {}

runThisFunctionInAnyCase();

省略失败的变量使代码在我看来有点太晦涩了。

在里面声明变量就可以了,不用担心= vs ==。

// keep going if we don't fail
if (bool failA = !executeStepA())      {}
else if (bool failB = !executeStepB()) {}
else if (bool failC = !executeStepC()) {}
else if (bool failD = !executeStepD()) {}
else {
     // success !
}

runThisFunctionInAnyCase();

这很模糊,但很紧凑:

// keep going if we don't fail
if (!executeStepA())      {}
else if (!executeStepB()) {}
else if (!executeStepC()) {}
else if (!executeStepD()) {}
else { /* success */ }

runThisFunctionInAnyCase();

假循环已经提到了,但我没有看到下面的技巧在给出的答案:你可以使用一个do{/*…*/} while(evalates_to_zero ());实现一个双向早出中断。使用break终止循环而不计算条件语句,而使用continue将计算条件语句。

你可以使用它,如果你有两种类型的终结,其中一条路径必须比另一条路径做更多的工作:

#include <stdio.h>
#include <ctype.h>

int finalize(char ch)
{
    fprintf(stdout, "read a character: %c\n", (char)toupper(ch));

    return 0;
}

int main(int argc, char *argv[])
{
    int ch;
    do {
        ch = fgetc(stdin);
        if( isdigit(ch) ) {
            fprintf(stderr, "read a digit (%c): aborting!\n", (char)ch);
            break;
        }
        if( isalpha(ch) ) {
            continue;
        }
        fprintf(stdout, "thank you\n");
    } while( finalize(ch) );

    return 0;
}

执行此命令会得到以下会话协议:

dw@narfi ~/misc/test/fakeloopbreak $ ./fakeloopbreak 
-
thank you
read a character: -

dw@narfi ~/misc/test/fakeloopbreak $ ./fakeloopbreak 
a
read a character: A

dw@narfi ~/misc/test/fakeloopbreak $ ./fakeloopbreak 
1
read a digit (1): aborting!

你可以把所有的if条件,按照你想要的格式放在它们自己的函数中,返回执行executeThisFunctionInAnyCase()函数。

从OP中的基本示例来看,条件测试和执行可以这样分离;

void InitialSteps()
{
  bool conditionA = executeStepA();
  if (!conditionA)
    return;
  bool conditionB = executeStepB();
  if (!conditionB)
    return;
  bool conditionC = executeStepC();
  if (!conditionC)
    return;
}

然后被这样称呼;

InitialSteps();
executeThisFunctionInAnyCase();

如果c++ 11 lambda是可用的(OP中没有c++ 11标记,但它们可能仍然是一个选项),那么我们可以放弃单独的函数,并将其包装为lambda。

// Capture by reference (variable access may be required)
auto initialSteps = [&]() {
  // any additional code
  bool conditionA = executeStepA();
  if (!conditionA)
    return;
  // any additional code
  bool conditionB = executeStepB();
  if (!conditionB)
    return;
  // any additional code
  bool conditionC = executeStepC();
  if (!conditionC)
    return;
};

initialSteps();
executeThisFunctionInAnyCase();

对于c++ 11及以上版本,一个很好的方法可能是实现一个类似D的作用域(退出)机制的作用域退出系统。

实现它的一种可能的方法是使用c++ 11 lambdas和一些helper宏:

template<typename F> struct ScopeExit 
{
    ScopeExit(F f) : fn(f) { }
    ~ScopeExit() 
    { 
         fn();
    }

    F fn;
};

template<typename F> ScopeExit<F> MakeScopeExit(F f) { return ScopeExit<F>(f); };

#define STR_APPEND2_HELPER(x, y) x##y
#define STR_APPEND2(x, y) STR_APPEND2_HELPER(x, y)

#define SCOPE_EXIT(code)\
    auto STR_APPEND2(scope_exit_, __LINE__) = MakeScopeExit([&](){ code })

这将允许你提前从函数返回,并确保你定义的任何清理代码总是在作用域退出时执行:

SCOPE_EXIT(
    delete pointerA;
    delete pointerB;
    close(fileC); );

if (!executeStepA())
    return;

if (!executeStepB())
    return;

if (!executeStepC())
    return;

宏实际上只是装饰。MakeScopeExit()可以直接使用。