C++ “避免的模式”;如果清理失败”;重复
我有一些代码如下所示:C++ “避免的模式”;如果清理失败”;重复,c++,c,design-patterns,C++,C,Design Patterns,我有一些代码如下所示: 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(); retur
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++中避免重复<代码>(ILR> 0){CURL1(A,B,C);CURL2(D,E);LogError();返回1;}在每个函数调用之后< <强> >/P>
注:
- 在实际代码中,这些函数
和DoSomethingx()
都是API函数,不是我自己编写的cleanx()
- 我希望避免在
之外定义第二个函数myfunct()
,该函数将处理cleanup+错误clean()
- 我曾考虑过使用预处理器宏,但我怀疑这对于这种情况是否是一种好的做法
就是这样一个例子:每10行代码实际上是=2行,仅用于实际执行某项操作+8行错误测试和清理。。。我们能做得更好吗?正如贾斯汀提到的,答案会因语言的不同而大不相同 如果你在C语言中,这是
goto
在该语言中占有一席之地的最后一个地方。例如,如果您有:
int myfunc()
{
胡说八道;
blah2 b;
blah3-c;
blah4d;
blah5-e;
int iRes=DoSomething1(a,b,c);
如果(iRes>0)
{
转到错误清除;
}
iRes=DoSomething2(a,c,e);
如果(iRes>0)
{
转到错误清除;
}
/*...*/
iRes=DoSomething10(c,d,e);
如果(iRes>0)
{
转到错误清除;
}
/*成功*/
清洁1(a、b、c);
清洁2(d,e);
返回0;
/*错误退出点*/
错误\u清除:
清洁1(a、b、c);
清洁2(d,e);
log_error();
返回1;
}
但是,在C++中,我们需要处理这个清理代码,即使抛出异常,也会让其他方案陷入困境。然而,在C++中,我们也有RAII,这意味着析构函数就是去路。
< P>正如贾斯廷所提到的,答案将是完全不同的,这取决于语言。 如果你在C语言中,这是goto
在该语言中占有一席之地的最后一个地方。例如,如果您有:
int myfunc()
{
胡说八道;
blah2 b;
blah3-c;
blah4d;
blah5-e;
int iRes=DoSomething1(a,b,c);
如果(iRes>0)
{
转到错误清除;
}
iRes=DoSomething2(a,c,e);
如果(iRes>0)
{
转到错误清除;
}
/*...*/
iRes=DoSomething10(c,d,e);
如果(iRes>0)
{
转到错误清除;
}
/*成功*/
清洁1(a、b、c);
清洁2(d,e);
返回0;
/*错误退出点*/
错误\u清除:
清洁1(a、b、c);
清洁2(d,e);
log_error();
返回1;
}
但是,在C++中,我们需要处理这个清理代码,即使抛出异常,也会让其他方案陷入困境。但是,在C++中,我们也有RAII,这意味着析构函数是要走的路。
< P>这是我喜欢的一个技巧,避免了Goto: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();
}
这里有一个我喜欢的技巧,可以避免跳槽:
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();
}
对于给定的代码,您可以在其上使用以下任意一种变体:
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;
}
如果在clean1()
和clean2()
之前或之后调用log\u error()
并不重要,您可以使用:
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
中的任何一个,因此很难确定什么是真正安全的。如果<代码> DOMMETHONGEN()/COD>函数在C++中并引用引用,则<代码> DOOMETHONGIN()/Cyto>可以初始化<代码> A<代码> <代码> B<代码> >代码> C>代码> ->对<代码> Cuff2()的调用>代码>可以发生在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;
}
如果在clean1()
和clean2()
之前或之后调用log\u error()
并不重要,您可以使用:
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
中的任何一个,因此很难确定什么是真正安全的。如果<代码> DOMMETHONGEN()/CUT>函数在C++中,并通过引用来引用,那么<代码> DOOMETHONGIONS(/CUT>)可以初始化<代码> A<代码> <代码> B<代码> >代码> C++ >代码>但是,对<代码> Cuff2()的调用>代码>可以在DoSomethingN
抛出异常,而不是通过返回值标记错误。如果DoSomethingN
超出了您的控制范围,因为它是一个外部库,您应该将它包装起来以抛出异常。@Basj这些问题必须是两个独立的问题。事实上,它标记为[C++ +]和[c]使得C++或C++使用得太宽了吗?答案会因你是谁而大不相同using@Basj他们将非常不同。在C++中,你将使用RAII。C没有RAIIIn的C++案例,如果错误不太常见,就会抛出并捕获异常。这将收集大部分(如果不是全部的话)@Basj基本上,你会让DoSomethingN
抛出一个例外