It might be easier to write a program assuming there is nothing that will go wrong, but sometimes it’s easy to overlook coding errors or it may be that the errors that may happen are out of your control.
A common example of this is when you use your mobile phone. You dial in a number that is not already in your contacts list but instead of it ringing a message comes through that the number you have dialled is incorrect or no longer assigned. The workings behind the interface you see and interact with, have flagged the problem and sent you a pre-recorded message. If you misdialed the number, you would get a message asking you to try again. This is Exception Handling.
What is an exception?
An exception is a problem that arises during the running of a program. It’s a response to, an exceptional circumstance that arises while a program is running. It could be the result of a calculation trying to divide by zero or a user inputting incorrect information. Exceptions provide a way to react to exceptional circumstances in programs (such as runtime errors) by handing over control to special functions referred to as handlers that throw
the exception from inside a try
block. Handlers are declared with the keyword catch
, which are placed directly after the try
block. This topic is all about how this works in different circumstances
Exception Handling
Exception handling is a new feature of C++, and most C++ compilers support this new feature. Exception handling in C++ provides a way of handling unexpected circumstances in your program such as runtime errors. Whenever an error happens, the control of the program is transferred to special functions known as handlers.
There are two types of exceptions:
- Synchronous
- Asynchronous
Errors that occur internally or that are out of range, or deal with overflow etc., are synchronous type exceptions. Those errors cause outside of internal events and are beyond the control of the program are asynchronous exceptions. The main role of exception handling is to provide a way of detecting and reporting problems so the appropriate action can be taken. It needs to:
- find the problem (exception)
- identify and inform the problem has occurred (
throw
the exception) - receive the error information (
catch
the exception) - take action to correct the problem (handle exception)
The error code basically consists of two segments, one to detect errors and to throw
exceptions and the other to catch
the exceptions and take appropriate actions.
To catch
exceptions, code is written under a catch
block. There are based on three keywords:
try
– identifies the code block for which certain exceptions will be monitored and activated. This should be followed by one or morecatch
blocks. Functions called from within atry
block may alsothrow
an exception.throw
– when a program encounters a problem (i.e. an error), it throws an exception. Thethrow
keyword helps the program perform the throw.catch
– a program catches an exception using exception handlers. It’s added to the section of program where you need to handle the problem. It’s done using thecatch
keyword.
Syntax:
Exceptions mechanisms
Although there is only one try
statement, there can be more than one catch
block. The exception name is the name of the exception to be caught and the exception 1, exception etc are your defined names for referring to the exceptions.
- If an exceptional situation occurs within the
try
section of code, an exception is thrown so the handler takes over the program. - If the
try
block throws an exception, then program control shift from the block and enters into thecatch
statement of thecatch
block. - If the type of object thrown matches the argument in the
catch
statement, thecatch
block is executed to handle the exception. - You can design a program to
throw
as many exceptions as you want.
It works like this:
A throw
expression accepts one parameter which is passed to the exception handler. The exception handler is declared with the catch
keyword immediately after the closing bracket of the try
block. The syntax for catch
is like a regular function with one parameter. The type of this parameter is very important, as the type of the argument passed by the throw
expression is checked against it and only if they match, the exception will be caught by that handler.
Catch All:
Multiple catch
expressions (handlers) can be chained, each with their own parameter type. Only the handler whose argument matches the exception type specified in the throw
statement is executed.
Example:
In this situation the last catch
block (handler – catch all) would catch
any exception thrown of a type that isn’t already declared.
Note: Once a handle has been handled, the program execution begins after the try
-catch
block, not the throw
statement.
You can also use try
-catch
blocks within more external try
blocks. This is done using the expression throw
; with no arguments:
In this situation it is possible that an internal catch
block will forward the exception to its external level.
The try
block can be a short as a few statements within one function or as an all broad as enclosing the main()
function code within a try
block which effectively causes the entire program to be monitored for errors.
You can also throw
an exception from a function outside the try
block.
Output:
Start Inside try block Inside Xtest, test is: 0 Inside Xtest, test is: 1 Caught an exception -- value is: 1 End
A try
block can be localised to a function. When this is done, each time the function is entered the exception handling relative to that function is reset. Open the code example below and run the program.
- What is the output?
- Change
Xhandler(1)
value to a0
. What is the output now?
You will have seen that three exceptions were thrown and after each exception the function returned. When the function is called again the exception handling is reset. The code associated with a catch
statement will be executed ONLY if it catches an exception, otherwise execution simply bypasses the catch
altogether. Where an exception is not thrown, the catch
statement does not execute. It’s also described as ‘no match, no catch’.
Watch Codearchery as they explain exception handling using a bank deposit as an example so you understand basic coding around this new topic. It’s a great place to start.
Thenewnboston explains the basics of exceptions using code examples. You can also continue onto tutorial #63 More Exception Examples
Now it’s your turn. Write a small program based to demonstrate exception handling for two integers division for a Divde by zero error.
Practice 1
Write a simple C++ program to demonstrate exception handling for two integers division for a divide by zero error.
Here’s an example:
Why exception handling?
There are three main advantages of exception handling over tradition error handing:
- Separating error handling from normal code
- Functions/methods can handle any exceptions they choose
- Group error types
In traditional handling of errors there are always if else conditions to handle errors. These along with the code can get mixed up with the normal flow of the code. It makes the code difficult to read which means hard to maintain. When we use try
blocks, the code for the error becomes separate from the normal flow.
A function can throw
many exceptions but may choose to handle some of them. The others that are thrown but not caught can be handled by the caller. Or the caller of the caller. In C++ a function can specify the exceptions it throws by using the throw
keyword. The caller of this function MUST handle the exception in some way. This might be through specifying it again or catching it.
Both types and objects can be thrown as an exception in C++. We can create a hierarchy of exceptions objects, group exceptions in namespace or classes, and categorise them according to types.
Look at this simple exception handling example.
Run the code to find the output.
An explanation of the practice program:
There is a try block containing three statements and a catch(int i)
statement that processes an integer exception.
Within the try
block, only two of the three statements will execute: the first cout
statement and the throw
. Once an exception has been thrown, control passes to the catch
expression and the try
block is terminated.
The catch
is not called. Rather, program execution is transferred to it. This means the cout statement following the throw
will never execute.
Usually, the code within a catch
statement attempts to remedy an error by taking appropriate action. If the error can be fixed, execution will continue with the statements following the catch
. However, often an error cannot be fixed and a catch
block will terminate the program with a call to exit()
or abort()
.
Make a change to the program
What happens if you change the type in the catch
statement to double? Run the program to find out.
When the catch
does not match the throw
:
If you change the type in the catch
statement to double as you were asked to do in the exercise above, the exception will not be caught, and an abnormal termination will occur. The integer exception will not be caught by the catch
double statement and the output would show something similar to this:
Output:
Start Inside try block Abnormal termination
Also see this example:
Output:
Start Inside try block Still inside try block End
Catching class types
An exception can be of any type that you create, but in real world programs, most exceptions will be class types rather than built in types. The most common reason you will want to define a class type for an exception is to create an object that describes the error. This information can be used by the exception handler to help it process the error.
Output:
Enter a positive number: 4 Not positive: -4
Using multiple catch statements
We already know that is common to have more than one catch
associated with a try
, but each catch
must catch
a different type of exception. The following program catches both integers and stings.
You will use the program you changed previously for this practice.
Change the code in the program to match what’s below:
Did you get this output? As you can see each catch
statement only responded to its own type. Only matched statements are executed, all others are ignored.
Start Caught Exception #: 1 Caught Exception #: 2 Caught a string: Value is zero Caught Exception #: 3 End
Standard exceptions in C++
The C++ standard library provides a base class specifically designed to declare objects to be thrown as exceptions. It’s called std::exceptions
. This is defined in the <exception>
header.
The C++ std::exceptions
class allows us to define objects that can be thrown as exceptions. The class provides us with a virtual member function named what. This function returns a null-terminated character sequence of type char*. We can overwrite it in derived classes to have an exception description.
We have placed a handler that catches exception objects by reference (notice the ampersand & after the type), therefore this catches also classes derived from exception, like our myex object of type my exception.
Output:
My exception happened.
Exception | Description |
---|---|
std::exception |
An exception and parent class of all standard C++ exceptions |
std::bad_alloc |
An exception thrown by a new keyword |
std::bad_cast |
An exception thrown by dynamic_cast |
std::bad_exception |
Useful device for handling unexpected exceptions in C++ programs |
std::bad_typeid |
An exception thrown by typeid |
std::logic_error |
This exception is theoretically detectable by reading code |
std::domain_error |
Exception thrown after using a mathematically invalid domain |
std::invalid_argument |
An exception thrown for using invalid arguments |
std::length_error |
An exception thrown after creating a big std::string |
std::pit_of_range |
Thrown by at method |
std::runtime_error |
An exception that cannot be detected via reading the code |
std::overflow_error |
Exception thrown after the occurrence of a mathematical overflow |
std::range_error |
Exception thrown when you attempt to store an out-of-range value |
std::underflow_error |
Exception thrown after the occurrence of mathematical underflow |
For more information cpp exceptions handling, read more about it at guru99.com.
Exception specification: Pre C++
Older code may contain dynamic exception specifications. These are no longer used in C++, but are still supported. For example:
This declares a function names myfunction which takes one argument of type and returns a value of type double. Should this throw
an exception of some type other than int, the function call std::unexpected
(from the std::exception
parent class) rather than looking for the exception handler or calling on std::terminate
.
If the throw
specifier is left empty, without type this means that std::unexpected is call for any exception. Functions with no throw
specifier never call std::unexpected
, they follow the normal path for finding their exception handler. For example:
If statements:
If statements can be used to control exception handling:
While using if statements is one way of handling exceptions, in complex programming, it would not be practical to have if statements everywhere.
Catching all exceptions:
You may want an exception handler to catch
all exceptions instead of just a certain type
Output:
Start Caught One! Caught One! Caught One! End
Restricting Exceptions:
You can restrict the type of exceptions that a function can throw outside of itself. In fact, you can also prevent a function from throwing any exceptions whatsoever. To accomplish these restrictions, you must add a throw
clause to a function definition
Here, only those data types contained in the comma-separated type-list may be thrown by the function. Throwing any other type of expression will cause abnormal program termination. If you don't want a function to be able to throw
any exceptions, then use an empty list. Attempting to throw
an exception that is not supported by a function will cause the standard library function unexpected()
to be called. By default, this causes abort()
to be called, which causes abnormal program termination.
All three throws were caught using the one catch
statement. One very good use for catch(...)
is as the last catch
of a cluster of catches. In this capacity it provides a useful default or "catch all" statement. For example, this slightly different version of the preceding program explicitly catches integer exceptions but relies upon catch(...)
to catch all others. As this example suggests, using catch(...)
as a default is a good way to catch all exceptions that you don't want to handle explicitly. Also, by catching all exceptions, you prevent an unhandled exception from causing an abnormal program termination.
Output:
Start Caught an integer Caught One! Caught One! End
In this program, the function Xhandler()
may only throw
integer, character, and double exceptions. If it attempts to throw
any other type of exception, an abnormal program termination will occur. (That is, unexpected()
will be called.) To see an example of this, remove int from the list and retry the program.
It is important to understand that a function can be restricted only in what types of exceptions it throws back to the try
block that called it. That is, a try
block within a function may throw
any type of exception so long as it is caught within that function.
The restriction applies only when throwing an exception outside of the function. The following change to Xhandler()
prevents it from throwing any exceptions.
Practice 2
Write a c++ program with the following
- A member function to get two double type numbers from user
- A member function to calculate the division of these two numbers
- A
try
block tothrow
an exception class type when a wrong type of data is entered - A
try
block to detect andthrow
an exception if the condition divide by zero occurs