我有一些看起来像这样的代码:
int myfunc()
{
blah a;
blah2 b;
blah3 c;
blah4 d;
blah5 e;
int iRes = DoSomething1(a, b, c);
if (iRes > 0)
{
clean1(a, b, c);
clean2(d, e);
log_error();
return 1;
}
iRes = DoSomething2(a, c, e);
if (iRes > 0)
{
clean1(a, b, c);
clean2(d, e);
log_error();
return 1;
}
...
iRes = DoSomething10(c, d, e);
if (iRes > 0)
{
clean1(a, b, c);
clean2(d, e);
log_error();
return 1;
}
clean1(a, b, c);
clean2(d, e);
return 0;
}
如何在C或C ++中避免在每个函数调用后重复if (iRes > 0) { clean1(a, b, c); clean2(d, e); log_error(); return 1; }
?
注意:
DoSomethingx()
和cleanx()
是API函数,不是我自己编写的clean()
之外定义第二个函数myfunct()
来处理清理+错误示例:的
This code就是这种情况的一个例子:每10行代码实际上= 2行仅用于实际执行某些操作 + 8行错误测试和清理...我们可以做得更好吗?
答案 0 :(得分:3)
正如贾斯汀所说,答案将因语言而异。
如果您在C,这是goto
在该语言中具有据点的最终位置之一。例如,如果您有:
int myfunc()
{
blah a;
blah2 b;
blah3 c;
blah4 d;
blah5 e;
int iRes = DoSomething1(a, b, c);
if (iRes > 0)
{
goto error_cleanup;
}
iRes = DoSomething2(a, c, e);
if (iRes > 0)
{
goto error_cleanup;
}
/*...*/
iRes = DoSomething10(c, d, e);
if (iRes > 0)
{
goto error_cleanup;
}
/* SUCCESS */
clean1(a, b, c);
clean2(d, e);
return 0;
/* ERROR EXIT POINT */
error_cleanup:
clean1(a, b, c);
clean2(d, e);
log_error();
return 1;
}
但是,在C ++中,即使抛出异常,我们也需要处理这个清理代码,这会给事情带来另一个麻烦。但是,在c ++中我们也有RAII,这意味着析构函数是可行的方法。
答案 1 :(得分:2)
这是我喜欢的一个技巧,可以避免转到:
bool success = false;
do {
r = do_something();
if (r) break;
r = do_something_else();
if (r) break;
r = do_something_again();
if (r) break;
success = true;
} while(0);
if (! success) {
clean_up();
}
答案 2 :(得分:0)
对于给定的代码,您可以使用以下任意一种变体:
int myfunc()
{
blah a;
blah2 b;
blah3 c;
blah4 d;
blah5 e;
int iRes;
if ((iRes = DoSomething1(a, b, c)) > 0 ||
(iRes = DoSomething2(a, c, e)) > 0 ||
...
(iRes = DoSomething10(c, d, e)) > 0)
{
clean1(a, b, c);
clean2(d, e);
log_error();
return 1;
}
clean1(a, b, c);
clean2(d, e);
return 0;
}
如果在log_error()
和clean1()
之前或之后调用clean2()
无关紧要,您可以使用:
int myfunc()
{
blah a;
blah2 b;
blah3 c;
blah4 d;
blah5 e;
int iRes;
if ((iRes = DoSomething1(a, b, c)) > 0 ||
(iRes = DoSomething2(a, c, e)) > 0 ||
...
(iRes = DoSomething10(c, d, e)) > 0)
{
log_error();
}
clean1(a, b, c);
clean2(d, e);
return 0;
}
甚至:
int myfunc()
{
blah a;
blah2 b;
blah3 c;
blah4 d;
blah5 e;
int iRes;
if ((iRes = DoSomething1(a, b, c)) > 0 ||
(iRes = DoSomething2(a, c, e)) > 0 ||
...
(iRes = DoSomething10(c, d, e)) > 0)
{
// Don't need to do anything here
}
clean1(a, b, c);
clean2(d, e);
if (iRes > 0)
log_error();
return 0;
}
if
的正文可能是最终作业:
int myfunc()
{
blah a;
blah2 b;
blah3 c;
blah4 d;
blah5 e;
int iRes;
if ((iRes = DoSomething1(a, b, c)) > 0 ||
(iRes = DoSomething2(a, c, e)) > 0 ||
...
)
{
iRes = DoSomething10(c, d, e);
}
clean1(a, b, c);
clean2(d, e);
if (iRes > 0)
log_error();
return 0;
}
这种重构取决于清理在所有情况下都是相同的。通常,它不是那么系统化。由于您未初始化a
,b
,c
,d
或e
,因此很难确定哪些是真正安全的。如果doSomethingN()
函数在C ++中并通过引用获取参数,则doSomething1()
可以初始化a
,b
,c
- 但调用{{1}可能会在clean2()
或d
有值之前发生。