假设我有这样的伪代码:

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语句可以以某种方式使用吗?


当前回答

只需使用一个额外的函数来让你的第二个版本工作:

void foo()
{
  bool conditionA = executeStepA();
  if (!conditionA) return;

  bool conditionB = executeStepB();
  if (!conditionB) return;

  bool conditionC = executeStepC();
  if (!conditionC) return;
}

void bar()
{
  foo();
  executeThisFunctionInAnyCase();
}

使用深度嵌套的if(你的第一个变体)或者想要跳出“函数的一部分”通常意味着你确实需要一个额外的函数。

其他回答

你也可以这样做:

bool isOk = true;
std::vector<bool (*)(void)> funcs; //vector of function ptr

funcs.push_back(&executeStepA);
funcs.push_back(&executeStepB);
funcs.push_back(&executeStepC);
//...

//this will stop at the first false return
for (auto it = funcs.begin(); it != funcs.end() && isOk; ++it) 
    isOk = (*it)();
if (isOk)
 //doSomeStuff
executeThisFunctionInAnyCase();

通过这种方式,您可以获得最小的线性增长大小,每次调用+1行,并且易于维护。


编辑:(谢谢@Unda)我不太喜欢,因为在我看来你失去了能见度:

bool isOk = true;
auto funcs { //using c++11 initializer_list
    &executeStepA,
    &executeStepB,
    &executeStepC
};

for (auto it = funcs.begin(); it != funcs.end() && isOk; ++it) 
    isOk = (*it)();
if (isOk)
 //doSomeStuff
executeThisFunctionInAnyCase();

break语句可以以某种方式使用吗?

也许不是最好的解决办法,但你可以把你的陈述变成一种方式。While(0)循环并使用break语句而不是return。

让执行函数在失败时抛出异常,而不是返回false。然后你的调用代码看起来像这样:

try {
    executeStepA();
    executeStepB();
    executeStepC();
}
catch (...)

当然,我假设在您最初的示例中,执行步骤只会在步骤内发生错误的情况下返回false ?

因为你也有……代码块…]在执行之间,我猜你有内存分配或对象初始化。通过这种方式,你必须关心在退出时你已经初始化的所有东西,如果你遇到问题,任何函数都会返回false,也要清理它。

在这种情况下,根据我的经验(当我使用CryptoAPI时),最好的方法是创建小类,在构造函数中初始化数据,在析构函数中反初始化数据。下一个函数类必须是前一个函数类的子类。如果出错-抛出异常。

class CondA
{
public:
    CondA() { 
        if (!executeStepA()) 
            throw int(1);
        [Initialize data]
    }
    ~CondA() {        
        [Clean data]
    }
    A* _a;
};

class CondB : public CondA
{
public:
    CondB() { 
        if (!executeStepB()) 
            throw int(2);
        [Initialize data]
    }
    ~CondB() {        
        [Clean data]
    }
    B* _b;
};

class CondC : public CondB
{
public:
    CondC() { 
        if (!executeStepC()) 
            throw int(3);
        [Initialize data]
    }
    ~CondC() {        
        [Clean data]
    }
    C* _c;
};

然后在你的代码中你只需要调用:

shared_ptr<CondC> C(nullptr);
try{
    C = make_shared<CondC>();
}
catch(int& e)
{
    //do something
}
if (C != nullptr)
{
   C->a;//work with
   C->b;//work with
   C->c;//work with
}
executeThisFunctionInAnyCase();

我想这是最好的解决方案,如果每次调用ConditionX初始化一些东西,分配内存等。最好确保所有东西都被清理干净。

你可以使用&&(逻辑与):

if (executeStepA() && executeStepB() && executeStepC()){
    ...
}
executeThisFunctionInAnyCase();

这将满足你的两个要求:

executeStep<X>()应该只在前一个成功时才计算(这称为短路计算) executeThisFunctionInAnyCase()将在任何情况下执行