Handling Exceptions in C++ - C++

When you write code that is not a complete program (such as a class or a set of functions), unexpected errors can happen in your code. You can’t always count on the values that you expect, and you can’t always assume that users of your code will know what they are doing. A graceful piece of code will prevent a program from crashing when these kinds of things happen. One way to make your code “graceful” is by using exception handling, which enables you to manage non-routine circumstances in a program.

With exception handling, you can actually prevent unpredictable occurrences and exceptional events. Exceptions mean that a part of your code cannot perform a task that it was asked to do. Exception handling enables you to try another method of completing the task or to attempt to fix the problem.

An exception occurs because a section of code throws an exception. One example of an unexpected event, or exception, is a computer running out of hard drive space. However, running out hard drive space does not mean that a program will necessarily crash or completely fail at its task. You can write code that handles such exceptions by specifying that the program catch a certain kind of exception.

In order to catch an exception, you must use a try-block. A try-block is a section of code with the keyword try at the beginning. This keyword keeps track of whether an exception is thrown. The syntax for a try-block is as follows:

Using a try-block tells the computer that the code inside a try-block might throw an exception, so watch out.

After you establish a try-block, you can use it to catch an exception. You do this by using a catch statement. Here is the syntax for the catch statement:

Here exceptionName is the name of an exception. When the code inside a try-block throws an exception, the computer starts executing the code inside the catch statement. In order for a catch statement to be connected with a try statement, the catch statement must occur immediately after the try statement. You cannot have any code between them:

You can have as many catch statements after try statements as you want:

When an exception is thrown, the appropriate catch statement is called.

An exception is thrown using the throw statement. Here is the syntax:

throw exception;

Here exception is the exception.
exceptionName (from the catch statement) is the name of a type, any type at all. It can be int, char*, or string. In fact, it is very similar to a function argument, except that it doesn’t need a variable name.

exception must be an expression. Using a throw statement is like calling a function. Think of all the catch statements as an overloaded function. Based on the type of exception, the computer can decide which catch statement is the most appropriate one to use (if any).

Here is an example:

In the preceding example, the variable x is initialized to 10. Then the program checks to see whether x is 10 and throws an exception if so. This is obviously a useless example, but it illustrates our point.

The exceptionNames for the catch statements are not just type names. There is also a variable declaration, because catch statements work exactly like functions. The value of x (10) is passed to the catch statement so that the catch statement can deal with the error properly (it helps if the catch statement knows what the error is).

Also, even though there are two catch statements, the computer, using the rules for function overloading, figured out which statement is the correct one to call.

Using a built-in data type for an exception is usually not a good idea. When an integer is thrown, you can’t tell exactly what the error is. You might assume that the error is related to the value of an integer, but you can’t tell for certain.

Fortunately, there is a standard way to pass this information. Normally, you define a new type for every kind of exception. Here is an example:

Notice how this class is completely empty? This is because the class doesn’t need anything in it. The class is just being used for its name. Here is an example of how you might use this:

If your code throws an exception that is not caught (there is no appropriate catch statement), the computer decides what to do. Unfortunately, a computer is not very forgiving. It thinks the best way to handle an exception is to immediately close the program. To do so, the computer calls the terminate() or unexpected() functions; both are included with <exception>. Also, neither of the two have arguments or a void return type. To see what happens when these functions are called, compile and run the following program:

You might have noticed that the error message displayed is exactly the same as for the abort() function. This is because terminate() calls abort(). You can make terminate() call a custom function as well by calling the set_terminate() function. Here is the declaration:

typedef void (*terminate_function)();
terminate_function set_terminate(terminate function term_func);

This declaration looks really complicated, but you just pass the name of the function as the parameter. Here is an example:

Output:

There was an uncaught error.

Building an Exception Hierarchy

If you use really specific exceptions for a large program, you can quickly have an unmanageable number of exceptions. Having a ton of catch statements after every try-block is annoying at best. You can easily forget to include one and wind up with quite a problem. Fortunately, as you program, you will discover personal techniques to alleviate this problem.

However, one common way is to use inheritance (refer to, “Introducing Inheritance,” for more on that topic). If you make a hierarchy of exceptions, you can deal with only the most general one, rather than be specific. Or you can be very specific for some and very general for others. This technique gives you the flexibility to design your programs so that they adapt to meet any situation.

Here is an example of such a hierarchy:

With this hierarchy, you can choose to handle a specific exception, such as DivideByZero, or deal only with all math errors in one catch statement. Here is an example:

Output:

Whoops! You tried to divide by zero

Here you see that if you try to divide by zero, a DivideByZero exception is thrown, but if there is some other problem, a general MathError is thrown.

This example also illustrates another important point. If you call a function within a try-block and that function throws an error, the try-block can catch it.

Catching Every Exception

You can catch every possible exception in your programs, making them virtually crash-proof. To do so, you use a form of the catch statement that can handle any exception that hasn’t already been handled. Rather than an exception type as the argument for the catch statement, you use an ellipsis (...). When the computer searches the catch statements for one that will handle an exception, the computer will use this one as a last resort (if you include it). Here is the syntax:

Output:

There was an exception, but it was caught!

If you use this version of the catch statement with other catch statements, it must be at the end of the list. If not, no subsequent catch statements beyond the default catch statement will ever have the possibility of being called. However, if the default catch statement is used at the end of the list of catch statements, the default catch statement will be used only as a last resort.


All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

C++ Topics