C++异常处理解析3: 错误处理(返回值, 错误标志变量, 异常)各有千秋

前言:

程序设计里面至关重要的一块就是错误处理, C++异常处理是一种面向对象的机制, 期望将错误处理和错误检测分离. 这里我们结合其他两种错误处理方式(返回值, 错误标志变量)来分析一下不同的错误处理(包括返回值判断,  错误标志变量, 异常处理机制)各有什么优缺点以及各自的适用环境.

函数返回值判断错误处理:

这种错误处理和判断的方法基本上是使用一组错误处理的常量, 然后通过函数返回值, 把错误信息返回给函数调用者. 比如如下简单的代码:

const int invalidPara = -2;
const int outOfRange = -3;
const int other = -4;

int func(int para) {
    if(invalid parameter)
        return invalidPara;
    do something here;
    if(out of range)
        return outOfRange
    if(other error)
        return other;
}

这样的返回值判断的好处在于和系统API统一, 我们知道WinAPI以及Linux下面的系统函数都是以返回0(零)表示程序正常运行, 返回非零值表示不同的错误. 所以如果我们也采用这样的返回值判断的话可以和系统调用统一起来.

但是返回值判断错误的限制以及缺点也是很明显的(个人不是很推崇用返回值, 但是也还是要看具体情况). 首先呢, 返回值判断错误会破坏正常的返回值的作用, 使得函数调用不能被充分利用, 函数返回值不能作为其他表达式的组成部分, 因为这个返回值已经用来指示错误了而不是用来返回其他正常的计算结果, 即使可以既用于正常值计算又用于返回错误, 比如正常值都是正数, 错误值都是负数, 那这个结果还是不能直接被用作任何计算, 首先还是要判断这个是正常计算结果呢还是一个错误信息, 这就造成了计算的不方便.

其次很多时候其实是没办法使用返回值来判断错误信息的. 比如 1) 当func()返回类型是int的时候, 而且正常的结果的返回就是所有int型的值都有可能, 这个时候我们其实没法找到一个很好的int value 作为indicatro来指示这是个错误返回还不是一个正常的结果. 2) 编写范型的时候比如return T, 那怎么利用返回值来判断? 这个时候因为我们不明确T的类型, 所以也没有很好的办法利用一个明确的返回值来判断或者给出错误信息. 在这些情况下, 异常处理应该是更为合理的错误处理的方式. 我们后面第三条会再讲到。接下来可以看看第二种错误处理机制.

错误标志变量判断:

这个类型的错误判断基本上可以用下面的这段程序表示. 也就是设置一个错误标志变量, 然后通过引用或者指针的形式传递给被调用的函数, 函数一旦发现错误就设置这个标志, 上层调用者通过检查这个标志变量来判断是否有错误发生.

int funcCallee(int para, int &errorFlag) {
    if(invalid parameter)
        set errorFlag and return;
    do something here;
    if(out of range)
        set errorFlag and return;
    if(other error)
        set errorFlag and return;
}

int funcCaller(int para) {
    int errorFlag = 0;
    int ret = funcCallee(1, errorFlag);
    check errorFlag;
}

这个方法的好处在于现在我们的返回值值表示正常计算结果, 可以被方便的利用起来, 比起第一种利用返回值判断的话是一个比较明显的优势, 而且前面提到的两种不能使用返回值判断错误的情况(泛型, 正常结果返回涵盖所有整型), 我们也可以使用标志位. 因为表示为总是可以保证是int型的, 而且是不受函数的代码逻辑影响的, 基本上是一个独立的错误标志. 在我看来这种方法似乎并没有明显的缺陷. 我个人比较推重.

C++异常处理机制:

其实我觉得C++的异常处理就是我们这里说的第二种利用标志变量的面向对象版本的错误处理机制, 本质上似乎没有太大区别. 当然异常处理还有复杂的多精细的多. 两者都是统一的独立于程序业务逻辑的错误处理机制. 比如不管程序干什么(泛型也好, 其他什么也好), 我们遇到错误总是能够抛出一个异常, 终止当前函数, 把控制权转移给上层调用函数进行处理. 对应到我们的第二种错误标志变量的话, 就是检测到异常或者错误的时候, 正确设置标识变量, 然后return, 控制权也转移给上层调用函数, 上层调用函数通过判断标志变量的值来进行处理. 从这个角度来讲, 似乎两者也没有太大区别.

另一方面呢, 异常机制作为C++的一种语言级别的机制, 其实会有比较大的开销, 包括控制权的转移等等, 他的好处在于错误处理和错误逻辑分离的很清楚, 而且强制使用者一定要处理异常, 否则程序将最终终止. 但是方法二呢, 要是我忘记去检查那个错误标志变量了怎么办? 回答是不怎么办. 因为这个仅仅是代码级别的判断, 没有任何强制措施去要求一定要处理。 这个就是很危险的了. 所以异常机制(语言级别的判断)从这个角度来讲也是比较好的一种错误处理的选择.

结束语:

这篇文章我们还是解析C++的异常处理机制, 这里我们结合其他两种错误处理方式(返回值, 错误标志变量)分析了这些不同的错误处理, 即返回值判断,  错误标志变量, 异常处理机制各有什么优缺点以及各自的适用环境.

Written on September 29, 2013