是的,两个讨厌的构造结合在一起。它是否像听起来那么糟糕,或者它可以被视为控制 goto 使用并提供合理清理策略的好方法?
在工作中,我们讨论了是否在我们的编码标准中允许 goto。一般来说,没有人愿意允许免费使用 goto,但有些人对使用它进行清理跳转持积极态度。如这段代码:
void func()
{
char* p1 = malloc(16);
if( !p1 )
goto cleanup;
char* p2 = malloc(16);
if( !p2 )
goto cleanup;
goto norm_cleanup;
err_cleanup:
if( p1 )
free(p1);
if( p2 )
free(p2);
norm_cleanup:
}
这种使用的最大好处是您不必最终得到以下代码:
void func()
{
char* p1 = malloc(16);
if( !p1 ){
return;
}
char* p2 = malloc(16);
if( !p2 ){
free(p1);
return;
}
char* p3 = malloc(16);
if( !p3 ){
free(p1);
free(p2);
return;
}
}
尤其是在具有许多分配的类似构造函数的函数中,这有时会变得非常糟糕,尤其是当有人必须在中间插入一些东西时。
因此,为了能够使用 goto,但仍然清楚地将其与自由使用隔离开来,创建了一组流控制宏来处理任务。看起来像这样(简化):
#define FAIL_SECTION_BEGIN int exit_code[GUID] = 0;
#define FAIL_SECTION_DO_EXIT_IF( cond, exitcode ) if(cond){exit_code[GUID] = exitcode; goto exit_label[GUID];}
#define FAIL_SECTION_ERROR_EXIT(code) exit_label[GUID]: if(exit_code[GUID]) int code = exit_code[GUID];else goto end_label[GUID]
#define FAIL_SECTION_END end_label[GUID]:
我们可以这样使用它:
int func()
{
char* p1 = NULL;
char* p2 = NULL;
char* p3 = NULL;
FAIL_SECTION_BEGIN
{
p1 = malloc(16);
FAIL_SECTION_DO_EXIT_IF( !p1, -1 );
p2 = malloc(16);
FAIL_SECTION_DO_EXIT_IF( !p2, -1 );
p3 = malloc(16);
FAIL_SECTION_DO_EXIT_IF( !p3, -1 );
}
FAIL_SECTION_ERROR_EXIT( code )
{
if( p3 )
free(p3);
if( p2 )
free(p2);
if( p1 )
free(p1);
return code;
}
FAIL_SECTION_END
return 0;
它看起来不错,并且有很多好处,但是,在将其推广到开发中之前,我们应该考虑什么缺点吗?毕竟它是非常流量控制和 goto:ish。两人都气馁。在这种情况下阻止他们的论据是什么?
谢谢。