All of the articles in this series so far have been about writing and implementing programs in C++. Now we are going to talk about something a little bit different: we will explain a way to robustly handle errors in our program. This is called exception handling: the process of responding to exceptions – anomalous or exceptional conditions requiring special processing – often changing the normal flow of program execution. If there is an error in our program, we do not want it to crash - we want to be able to handle it as elegantly as possible.
In this article we will mention different ways of preventing failure. Some of them are built-in functions and using output as a control mechanism. Some classes have built-in methods used for checking bounds of data structures, break conditions etc. One example of this is the Stack
class we wrote in the last article. The class had these methods:
int empty() const
{
return t < 0;
}
int full() const
{
return t == CAPACITY - 1;
}
This approach can be viewed as a method of preventing failure, because we are taking into consideration the bounds and capacity of the data structure.
It is also possible to track the flow of your program by viewing the outputs at strategically placed execution points, but C++ provides a more sofisticated method for preventing failure - the exception mechanism.
Simply speaking, an exception is an event responsible for preventing the failure of our program. Data describing the exception can be represented either as a value, or in the form of an object. Exceptions provide a way to transfer control from one part of a program to another. C++ exception handling is built upon three keywords: try
, catch
, and throw
. When errors occur, the function generating the error can 'throw' an exception (exceptions are simple classes containing the description of the error). Now we are supposed to 'catch' the thrown exception, and to do this, we will use the try-catch mechanism. Unlike the throwing, which usually happens in the body of the function that might cause failure, the try-catch block is used when we are calling such a function. Syntax:
type myMethod()
{
...
if(errorOcurred)
throw MyException("My error message");
}
/*calling the method somewhere in the code*/
try
{
...
type t = myMethod();
...
}
catch(MyException& e)
{
cout << e.errorMessage;
}
The
catch
statement catches exceptions that are of the proper type. You can, for example, throw objects of different classes to differentiate between several different exceptions. Y ou can also catch any type of exceptions with the universal handler:
catch(...)
{
//catches all exceptions
}
The catch
block always has to be placed right after the try
block.
The real advantage of using exception handling is the fact that, once a catch statement is executed, the program continues to run from the end of the catch. This provides a way to continue running the program without crashing, even though an error has occured.