我最近进行了一次代码审查,并开始辩论。我的大部分代码都是这样的:
for (i = 1; i <= 3; i++)
{
DoubleValue = tODBCX->getDouble(KeyFieldCount + i, IsNULL, IsSuccess);
if (IsNULL)
{
LoggerDrillHole::LogToDB(LOG_ERROR, L"Survey depth, dip and azimuth values can not be NULL.", __FUNCTIONW__);
IsSuccess = false;
goto EXIT;
}
else
{
if (i == 1)
Depth = DoubleValue;
else if(i == 2)
DipDegrees = DoubleValue;
else if (i == 3)
AzimuthDegrees = DoubleValue;
}
}
争议性的goto声明引发了争论。此代码包含在一个函数中,该函数通过初始化本地布尔变量IsSuccess = true开始生命,函数最终返回IsSuccess。 EXIT策略负责处理必要的整理代码;
EXIT:
tODBCX->Close();
if (Key != 0) free(Key);
Key = 0;
if (PriorKey != 0) free(PriorKey);
PriorKey = 0;
return IsSuccess;
有几个这样的goto EXIT语句与设置IsSuccess = false相邻并记录到数据库等等。有人评论说,这会破坏代码的流程。应该使用do循环(无限循环)并中断该循环,然后处理所有必需的而不是使用goto语句。
我非常不喜欢无限循环策略,但如果能真正提高可读性,我就会习惯它。还有更好的方法吗?
答案 0 :(得分:4)
我想将此标记为this question的副本。尽管解决方案是相同的,但它仍然不完全相同:
在C ++中,最好的解决方案是使用RAII和事务代码。在C中,最好的解决方案是使用goto,遵循一些规则(仅用于返回/清理,不使用goto来模拟循环等)。
请参阅上述问题中的my answer(基本上,解决方案是使用RAII和事务代码);这将完全消除goto清理/错误处理块的需要。
答案 1 :(得分:4)
在这里使用goto
没有错。这是最简洁的解决方案之一。 (另一个例子是突破内循环。)
使用do { ... } while (false)
循环是一种人工解决方案,实际上会降低代码的可读性。
答案 2 :(得分:0)
在C中,考虑将代码分解为两个函数...一个执行常见初始化的外部函数,并传递内部函数需要的变量,这样内部函数可以简单地返回成功状态,知道外部函数将清理起来。
在C ++中,使用范围保护通常是个好主意,因此析构函数可以确保正确清理。考虑一下你的:
tODBCX->Close();
如果tODBCX
需要比函数调用更长寿 - 所以析构函数中的Close()
没有帮助 - 然后创建一个帮助器:
struct Odbc_Access_Guard
{
Odbc_Access_Guard(ODBC& o) : odbc_(o) { }
~Odbc_Access_Guard() { odbc_.close(); }
operator ODBC&() { return odbc_; }
operator const ODBC&() const { return odbc_; }
ODBC& odbc_;
};
然后在你的功能中:
Odbc_Access_Guard odbc(tODBC);
odbc.xyz();
if (whatever)
return ...success expression...;
你的指针也是如此:它们应该是使用上述逻辑的共享指针或守卫。然后你可以随时返回,而不必考虑清理代码的位置,并想知道它是否与当前变量的使用保持同步。