简体   繁体   中英

Correct Exceptions in C++

I am just learning how to handle errors in my C++ code. I wrote this example that looks for a text file called some file, and if its not found will throw an exception.

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
int array[90];
try
{
   ifstream file;
   file.open("somefile.txt");
   if(!file.good())
    throw 56;
}
catch(int e)
{
    cout<<"Error number "<<e<<endl;
}
return 0;
}

Now I have two questions. First I would like to know if I am using Exceptions correctly. Second, (assuming the first is true) what is the benefit to using them vs an If else statement?

"Correctly" is a value judgment, but (unlike other classes) there's a major benefit from exceptions classes being a monolithic hierarchy, so I'd generally advise throwing something derived from std::exception , not simply an int.

Second, it's open to question whether an incorrect file name is sufficiently unexpected to qualify as a good reason to throw an exception at all.

As to benefits vs. an if/else statement: there are a couple. First, exceptions let you segregate the code that deals with errors, so the main idea and readability of the code don't get lost in a maze of error handling. Second, when you have several layers of code between throwing and catching the exception, the code that throws the exception may not know how it should really be handled. Your code, for example, uses std::cout to report the problem -- but most such code would report errors on std::cerr instead. You can change from one to the other without any change to the code that tried to open the file (which might be deep in a library, and have no clue of which should be used for this application -- and might be used in an application where both are wrong, and MessageBox was preferred).

First I would like to know if I am using Exceptions correctly.
Yes, though generally you want your exceptions to derive from std::exception.

Second, (assuming the first is true) what is the benefit to using them vs an If else statement?
For the given example, nothing. The benefit of exceptions comes when you have many deep nested functions, like this.

#include <stdexcept>
#include <iostream>
#include <string>

void anErrorFunc(const std::string& x)
{
    ifstream file;
    file.open(x);
    if (!file)
        throw std::runtime_error("Could not open file");
}

void someOtherFunction(const std::string& y)
{
    //Do stuff
    anErrorFunc(y);
    //Do other stuff
}

int main()
{
    try {
        someOtherFunction("somefile.txt");
    } catch (std::exception &ex) {
        std::cout << "Ouch! That hurts, because: "
            << ex.what() << "!\n";
    }
}

Note that the exception will be caught in main() , and someOtherFunction does not have to worry about dealing with passing through failure return codes.

Well, you are using exception correctly in that there is nothing wrong with your code. That said, typically we do not throw primitive types around (even though you can). It is generally a better idea to throw an object that derives from std::exception , and even better to throw an std::exception that is also a boost::exception .

When things are very simple and the handling code and the throwing code are in the same function, then there really is no reason to use exceptions instead of if statements (and, indeed, it would be faster and more efficient to use if...else in that particular case). However, in most situations, the point where the error is discovered and you need to report it is far removed from the logic where the error is to be handled. In many cases the error-recovery logic is specific to the application in question and the logic where the error is discovered cannot make a sensible choice about how to recover from the error, hence the need to throw.

Another benefit of exception handling is that the type of the exception can be used to convey the type of error that occurred. Usually the types in exception hierarchies are much more meaningful than those error codes that end up being used in C code. Also, you cannot ignore an exception as easily as you can ignore an error code; while you can ignore an exception, it will cause the program to die a horrible death. By contrast, if a C function returns an error status code, and you ignore it, it's possible to continue executing and get silently wrong results... in that sense, the use of exceptions is much safer than using error codes.

You may also be interested in reading about exceptions and error handling from the C++ FAQ Lite .

You are not using exceptions correctly. Your code has a much simpler equivalent, without exceptions, which provides the same behaviour.

Exceptions are for when you can't test the results of a function call with other methods. In this case you can, so you shouldn't use exceptions.

As a corollary, you shouldn't throw and catch the exception in the same function body - just do whatever you want to do instead of throwing it.

Syntactically speaking, your code is correct. Idiomatically speaking, maybe not so much -- or at least this depends on the context. When a file can't open then we might do our handling right there inside that if( !file.good() ) if it's perfectly common and possible to happen. For example, if the user asks to open a file in a text editor then it's perfectly plausible and common that the file doesn't exist. On the other hand, if the editor can't find the spelling corpus file then that means something is (arguably) terribly wrong. The program wasn't probably installed, or the user messed around with that file -- anything is possible.

In C++ we use exceptions for exceptional cases . That is, cases that are really not meant to happen and that we don't "accept" to happen. This is as opposed to a user file not opening, or an invalid user input, or no internet connection: these are all examples of perfectly valid things happening, common situations, things we expect to happen sooner or later in a program's run. They aren't exceptional .

Now, what are the benefits of using exceptions compared to conditionals? Allow me to extend this question to any other jump ( goto ) mechanism: returns as well as conditionals. Exceptions are more expressive if that's what you want to say: if you are dealing with an exceptional case then use exceptions. Exceptions also get more done than plain conditionals, in an analogous way to that of virtual functions getting more done than conditionals. The right code-block will be executed depending on the exception, but also the right scope will handle the exception depending on the handlers.

There are other advantages to exceptions compared with conditionals: exceptions separate error-handling from other code. They allow associating arbitrary data and actions with an error state. They allow communicating success states (via a return ) as well as error state (via a throw ). And the list goes on...

Technically speaking and at the lowest level exceptions are a sophisticated jump mechanism. Back in the butterfly-days people invented the if conditional as a somewhat-sophisticated goto in order to enhance expressiveness (because a goto can be used for anything really) and to reduce programmer errors. The looping constructs such as the C for loop are also in essence a sophisticated jump with sparkles and rainbow colours, again for reducing errors and enhancing expressiveness. C++ introduced its new casting operators for the same reasons.

So there: exceptions aren't magic, just something somewhat new in the scene compared to conditionals and loops. Don't use them when you don't mean to, just like you don't use a loop when you really mean to use a conditional.

Syntactically what you're doing is right. Style-wise, as others have noted, you should be throwing something descended from std::exception.

As to part two of your question, I'd like to go into more detail on that.

The whole point of exceptions is to separate policy from implementation. As Billy ONeal said, you get no benefit at all from using exceptions within the same function that an if statement wouldn't make better. You need to be deeply nested in function calls for it to make sense.

In most code, your high level code has enough information and context to know what to do about errors, but no mechanism to detect them. Your low level code can detect the errors but has none of the information needed to deal with them.

The traditional means of coping with this -- returning error codes -- has a few problems:

  1. It clutters up the code with error handling code to the point that the actual logic is obfuscated.
  2. It relies on programmers not being lazy and checking EVERY error code return, an often foolhardy assumption. (C programmers, be honest here: when was the last time you checked the return value of printf ?)
  3. It adds the overhead of error checking and handling to EVERY function call whether there's an error or not.

Exceptions solve these issues (with varying degrees of success).

  • Exceptions solve #1 by only having exception-related code at the point of detection and at the point of handling. Intervening functions don't get cluttered with handling for obscure errors that they themselves have no interest in nor capability of dealing with.
  • They solve #2 by forcing handling. You can't ignore an exception. You have to take action on them. (Lazy programmers can still catch all exceptions and then ignore them, but here their crippling inability to program is now highlighted for all to see.)
  • They solve #3 (when not naively implemented) by having near-zero costs when not used, albeit often at a very, very high cost when actually used.

This is not to say that exceptions are the end-all/be-all of error handling. The disadvantages:

  1. Exceptions are usually very costly when used. They have to be eschewed, despite their advantages, if performance is paramount.
  2. Exceptions lead to very opaque code at times. They are non-local transfers of control -- effectively slightly safer versions of goto statements, but across functions. An exception can transfer control from hundreds of layers deep in your code in source files not even slightly related to the ones you're working on (and, in fact, quite possibly not even accessible to you). This kind of "spooky action at a distance" can make code very difficult to figure out.
  3. "Checked exceptions" can actually be worse for noise generation than the old-style if handling. They tend to be more verbose, you see, than if or switch statements and the fact that you must handle checked exceptions for the code to even compile makes them a liability to many situations.
  4. Because of their often high cost of use, carelessly using them for all error handling can make your code slow and bloated.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM