C++的好处是什么使用的例外情况,并尝试/赶上,而不只是返回的一个错误代码?

StackOverflow https://stackoverflow.com/questions/196522

我编程的C和C++很长一段时间,到目前为止,我从未使用的例外情况,并尝试/赶上。有什么好处的使用,而不是仅仅有的功能返回的错误代码?

有帮助吗?

解决方案

可能是一个显而易见的观点 - 开发人员可以忽略(或不知道)您的退货状态,并继续幸福地发现某些事情失败。

需要以某种方式承认异常 - 如果没有积极地放置某些内容,就不能无声地忽略它。

其他提示

优势的例外是两倍:

  • 他们不能被忽略。 你必须处理他们在某一水平,或者他们会终止程序。错误代码,必须明确地检查他们的,或它们丢失。

  • 他们可以忽略。 如果错误不能予以处理在一个级别,它将自动泡到一个新的水平,它可能是。错误代码必须是明确通过,直到他们达到的水平,它可以加以处理。

优点是您不必在每次可能失败的呼叫后检查错误代码。为了实现这一点,您需要将它与RAII类结合使用,以便在堆栈展开时自动清理所有内容。

有错误消息:

int DoSomeThings()
{
    int error = 0;
    HandleA hA;
    error = CreateAObject(&ha);
    if (error)
       goto cleanUpFailedA;

    HandleB hB;
    error = CreateBObjectWithA(hA, &hB);
    if (error)
       goto cleanUpFailedB;

    HandleC hC;
    error = CreateCObjectWithA(hB, &hC);
    if (error)
       goto cleanUpFailedC;

    ...

    cleanUpFailedC:
       DeleteCObject(hC);
    cleanUpFailedB:
       DeleteBObject(hB);
    cleanUpFailedA:
       DeleteAObject(hA);

    return error;
}

使用例外和RAII

void DoSomeThings()
{
    RAIIHandleA hA = CreateAObject();
    RAIIHandleB hB = CreateBObjectWithA(hA);
    RAIIHandleC hC = CreateCObjectWithB(hB);
    ...
}

struct RAIIHandleA
{
    HandleA Handle;
    RAIIHandleA(HandleA handle) : Handle(handle) {}
    ~RAIIHandleA() { DeleteAObject(Handle); }
}
...

乍一看,RAII / Exceptions版本似乎更长,直到您意识到清理代码只需要编写一次(并且有方法可以简化)。但DoSomeThings的第二个版本更清晰,更易于维护。

不要在没有RAII习语的情况下尝试在C ++中使用异常,因为您会泄漏资源和内存。所有清理工作都需要在堆栈分配对象的析构函数中完成。

我意识到还有其他方法可以进行错误代码处理,但它们最终看起来有点相同。如果你放弃了这些,你最终会重复清理代码。

错误代码的一个要点是,它们可以明显地说明事情可能会失败,以及它们如何失败。在上面的代码中,您假设事情不会失败(但如果它们确实存在,您将受到RAII包装器的保护)。但是你最不应该注意事情可能出错的地方。

异常处理很有用,因为它可以很容易地将错误处理代码与为处理程序功能而编写的代码分开。这使得阅读和编写代码更容易。

除了提到的其他内容之外,您无法从构造函数返回错误代码。也可以是析构函数,但是你也应该避免从析构函数中抛出异常。

  • 返回错误代码错误时的条件是 预期 在某些情况下
  • 扔一个例外,当一个错误的条件是 预期在任何情况下

在前一种情况下调用者的功能必须检查的错误代码,用于预期的失败;在后一种情况下的例外,可以通过任何呼叫者的堆(或默认的处理程序)为是合适的

I wrote a blog entry about this (Exceptions make for Elegant Code), which was subsequently published in Overload. I actually wrote this in response to something Joel said on the StackOverflow podcast!

Anyway, I strongly believe that exceptions are preferable to error codes in most circumstances. I find it really painful to use functions that return error codes: you have to check the error code after each call, which can disrupt the flow of the calling code. It also means you can't use overloaded operators as there is no way to signal the error.

The pain of checking error codes means that people often neglect to do so, thus rendering them completely pointless: at least you have to explicitly ignore exceptions with a catch statement.

The use of destructors in C++ and disposers in .NET to ensure that resources are correctly freed in the presence of exceptions can also greatly simplify code. In order to get the same level of protection with error codes you either need lots of if statements, lots of duplicated cleanup code, or goto calls to a common block of cleanup at the end of a function. None of these options are pleasant.

Here's a good explanation of EAFP ("Easier to Ask for Forgiveness than Permission."), which I think applies here even if it's a Python page in Wikipedia. Using exceptions leads to a more natural style of coding, IMO -- and in the opinion of many others, too.

When I used to teach C++, our standard explanation was that they allowed you to avoid tangling sunny-day and rainy-day scenarios. In other words, you could write a function as if everything would work ok, and catch the exception in the end.

Without exceptions, you would have to get a return value from each call and ensure that it is still legitimate.

A related benefit, of course, is that you don't "waste" your return value on exceptions (and thus allow methods that should be void to be void), and can also return errors from constructors and destructors.

Google's C++ Style Guide has a great, thorough analysis of the pros and cons of exception use in C++ code. It also indicates some of the larger questions you should be asking; i.e. do I intend to distribute my code to others (who may have difficulty integrating with an exception-enabled code base)?

Sometimes you really have to use an exception in order to flag an exceptional case. For example, if something goes wrong in a constructor and you find it makes sense to notify the caller about this then you have no choice but to throw an exception.

Another example: Sometimes there is no value your function can return to denote an error; any value the function may return denotes success.

int divide(int a, int b)
{
    if( b == 0 )
        // then what?  no integer can be used for an error flag!
    else
        return a / b;
}

The fact that you have to acknowledge exceptions is correct but this can also be implemented using error structs. You could create a base error class that checks in its dtor whether a certain method ( e.g. IsOk ) has been called. If not, you could log something and then exit, or throw an exception, or raise an assert, etc...

Just calling the IsOk on the error object without reacting to it, would then be the equivalent of writing catch( ... ) {} Both statement would display the same lack of programmer good will.

The transport of the error code up to the correct level is a greater concern. You would basically have to make almost all methods return an error code for the sole reason of propagation. But then again, a function or method should always be annotated with the exceptions it can generate. So basically you have to same problem, without an interface to support it.

As @Martin pointed out throwing exceptions forces the programmer to handle the error. For example, not checking return codes is one of the biggest sources of security holes in C programs. Exceptions make sure that you handle the error (hopefully) and provide some kind of recover path for your program. And if you choose to ignore an exception rather than introduce a security hole your program crashes.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top