使用goto作为错误处理策略

时间:2014-11-28 13:34:57

标签: c++ c

我最近进行了一次代码审查,并开始辩论。我的大部分代码都是这样的:

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语句。

我非常不喜欢无限循环策略,但如果能真正提高可读性,我就会习惯它。还有更好的方法吗?

3 个答案:

答案 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...;

你的指针也是如此:它们应该是使用上述逻辑的共享指针或守卫。然后你可以随时返回,而不必考虑清理代码的位置,并想知道它是否与当前变量的使用保持同步。