假设我有这样的伪代码:
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++中都是自动化的。
在c++中,实际上有一种方法可以延迟操作:使用对象的析构函数。
假设你可以访问c++ 11:
class Defer {
public:
Defer(std::function<void()> f): f_(std::move(f)) {}
~Defer() { if (f_) { f_(); } }
void cancel() { f_ = std::function<void()>(); }
private:
Defer(Defer const&) = delete;
Defer& operator=(Defer const&) = delete;
std::function<void()> f_;
}; // class Defer
然后使用这个工具:
int foo() {
Defer const defer{&executeThisFunctionInAnyCase}; // or a lambda
// ...
if (!executeA()) { return 1; }
// ...
if (!executeB()) { return 2; }
// ...
if (!executeC()) { return 3; }
// ...
return 4;
} // foo
因为你也有……代码块…]在执行之间,我猜你有内存分配或对象初始化。通过这种方式,你必须关心在退出时你已经初始化的所有东西,如果你遇到问题,任何函数都会返回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初始化一些东西,分配内存等。最好确保所有东西都被清理干净。
有几个回答暗示了我看到并使用过很多次的模式,尤其是在网络编程中。在网络堆栈中,经常有很长的请求序列,其中任何一个都可能失败并停止进程。
常见的模式是使用do {} while (false);
我使用宏while(false)使它做{}一次;常见的模式是:
do
{
bool conditionA = executeStepA();
if (! conditionA) break;
bool conditionB = executeStepB();
if (! conditionB) break;
// etc.
} while (false);
这种模式相对容易阅读,并且允许使用能够正确销毁的对象,还避免了多次返回,从而使步进和调试更容易一些。
Don't. Sometimes you need the complexity. The trick is how you do it. Having the "what you do when the condition exists" may take up some room, making the if statement tree appear larger than it really is. So instead of doing things if a condition is set, just set a variable to a specific value for that case( enumeration or number, like 10,014. After the if tree, then have a case statement, and for that specific value, do whatever you would have done in the if tree. It will lighten up the tree.
if x1
if x2
if x3
Var1:=100016;
endif
endif
end if
case
var=100016
do case 100016 things...