C++ Exception Handling

Errors in C++ can be broadly categorized into two types —

  1. Compile Time Errors
  2. Run Time Errors

Compile Time Errors Errors caught during compile time is called Compile time errors. They errors include library reference, syntax error or incorrect class import.

Run Time Errors They are also known as exceptions. An exception caught during runtime creates serious issues.

C++ exception handling is used to handle exceptional conditions in a program systematically by taking the necessary actions. Therefore stated simply, the exception-handling capability of C++ makes it possible for us to:

• Monitor for exceptional conditions within our program.

• Transfer control to special exception-handling code if an exceptional condition occurs.

This is accomplished using three keywords: try, catch and  throw.  The basic concept is as follows:

• We try to execute the statements contained within a block of code (exception thrower).

• We catch and process the exception object using code that we have designed (exception handler).

• If we want to create an exceptional condition explicitly within a block of code, we use the throw statement.

 

Understanding the problem

To illustrate how an exception causes abnormal termination of execution of program, let us take a programming example. The following C++ program (except01.cpp) is syntactically correct and therefore there is no problem during compilation. But, while executing, it displays the following message (see the output below) and terminates without executing further statements.

#include <iostream>
using namespace std;

int divide(int a, int b) {
int c;
if(b == 0) {
// throw custom exception
throw "Division by zero";
}
else {
c = a / b;
}
return c;
}

int main() {
int a = 10, b = 0, c;

c = divide(a, b);
cout << "Result: " << c << endl;

c = divide(100, 25);
cout << "Result: " << c << endl;

c = divide(100, 5);
cout << "Result: " << c << endl;

return 0;
}

Output of the program is given below:

terminate called after throwing an instance of 'char const*'

The program works as follows:

When the runtime system detects the attempt to divide by zero (within divide() method), it constructs a new exception object and then throws this exception. This exception object passes onto its caller (the main() method). But, main() is not being able to handle this. Therefore, it causes the execution to stop, because once an exception has been thrown, it must be caught by an exception handler and dealt with immediately. In this example, we haven’t supplied any exception handlers of our own, so the exception is caught by the runtime system. The runtime system displays a message describing the exception and abnormally terminates the program without executing the remaining code statements.

 

Finding the solution

After a method throws an exception, the runtime system attempts to find some mechanism to handle it. C++ exception handling is used to handle exceptional conditions in a program systematically by taking the necessary actions. The purpose of exception handling mechanism is to provide a means to detect and report exceptional conditions so that appropriate actions can be taken. The exception handling mechanism basically consists of two segments, one to detect unexpected events and to throw exceptions and the other to catch exceptions and to take appropriate actions. This can be done in C++ by using try-catch block.

 

The try-catch block

C++ causes the keyword try to preface a block of code that is likely to cause an abnormal condition and “throw” an exception. A catch block defined by the keyword catch “catches” the exception thrown by the try block and handles it appropriately. The catch block is added immediately after the try block. This is the general form of an exception-handling try-catch block:

try {
// block of code that generates an exception
throw parameter;
}
catch (Exception-Type e) {
// block of code that handles the exception for Exception-Type
}

A try and its catch statement form a unit. The scope of the catch clause is restricted to those statements specified by the immediately preceding try statement. A catch statement cannot catch an exception thrown by another try statement (except in the case of nested try statements). The statements that are protected by try must be surrounded by curly braces (that is, they must be within a block). We cannot use try on a single statement. The goal of most well-constructed catch clauses should be to resolve the exceptional condition and then continue on as if the error had never happened.

Remember that every try statement should be followed by at least one catch statement; otherwise compilation error will occur. Another important thing is that catch statement works like a method definition. The catch statement is passed a single parameter, which is reference to the exception object thrown (thrown by the try block). If the catch parameter matches with the type of exception object, then the exception is caught and the statements within the catch block will be executed. Otherwise, the exception is not caught and the runtime system will cause the execution to terminate.

The following code gives solution to the problem discussed in the previous section (except02.cpp).

#include <iostream>
using namespace std;

int divide(int a, int b) {
int c;
if(b == 0) {
// throw custom exception
throw "Division by zero";
}
else {
c = a / b;
}
return c;
}

int main() {
int a = 10, b = 0, c;

try { //exception thrower
c = divide(a, b);
cout << "Result: " << c << endl;
}
catch (const char* ex) { //exception handler
cout << ex << endl;
}

c = divide(100, 25);
cout << "Result: " << c << endl;

c = divide(100, 5);
cout << "Result: " << c << endl;

return 0;
}

Output of the program is given below: 

Division by zero
Result: 4
Result: 20

The program works as follows:

Notice that the cout statement inside the try block is never executed. Once an exception is thrown, program control transfers out of the try block into the catch block. Put differently, catch is not “called,” so execution never “returns” to the try block from a catch. Thus, the cout statement inside the try block is never displayed. Once the catch statement has executed, program control continues with the next line in the program following the entire try-catch mechanism. Here the cout statement inside the catch block displays the exception object ex and the code statements following the catch block are executed normally.

 

Multiple catch blocks

It is possible to have more than one catch statement in the catch block as illustrated below in the following code segment:         

try {
// block of code that generates an exception
}
catch (Exception-Type1 e1) {
// block of code that handles the exception for Exception-Type1
}
catch (Exception-Type2 e2) {

// block of code that handles the exception for Exception-Type2
}
. . .
. . .
catch (Exception-TypeN eN) {
// block of code that handles the exception for Exception-TypeN
}
//Exception-TypeN is the type of exception that has occurred

 

When an exception in a try block is generated, the C++ treats the multiple catch statements like cases in a switch statement. The first catch statement whose parameter matches with the exception object will be executed, and the remaining statements will be skipped.

On exit from a catch block, normal execution continues unless there is any pending exception that has been thrown and not handled. If this is the case, the method is aborted and the exception is propagated up the runtime stack.

Let’s see a coding example (except03.cpp):

#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;

void method() {
// Initialize random number generator
srand(time(0));

// Generate a random number between 1 and 10
int n = (rand() % 10) + 1;
cout << "n = " << n << endl;

if(n < 4) {
throw 101;
}
else if(n < 7) {
throw 123.75;
}
else {
throw "error";
}
}

int main() {
try {
method();
}
catch (int ex) {
cout << "Caught : " << ex << endl;
}
catch (double ex) {
cout << "Caught : " << ex << endl;
}
catch (const char* ex) {
cout << "Caught : " << ex << endl;
}
return 0;
}

The output will vary depending on the value of ‘n’ which is generated randomly by using the rand() and srand() methods (available from <cstdlib>). For any particular exception being thrown, the runtime system will be looking for the matching catch block.

The program written above can be modified using the “generalized catch block” instead of having multiple catch blocks in the main() method. Basically, C++ introduces a feature which allows us to perform this, using the “three dots” notation (...) inside the catch block, which can handle any type of exception in a program:

try {
method();
}
catch (...) {
cout << "Exception caught";
}

 

Standard exceptions

The C++ Standard library provides a base class specifically designed to declare objects to be thrown as exceptions. It is called std::exception and is defined in the <exception> header. This class has a virtual member function called what() that returns a null-terminated character sequence (of type char *) and that can be overwritten in derived classes to contain some sort of description of the exception.

All exceptions thrown by components of the C++ Standard library throw exceptions derived from this exception class. These are:

exceptiondescription
bad_allocthrown by new on allocation failure
bad_castthrown by dynamic_cast when it fails in a dynamic cast
bad_exceptionthrown by certain dynamic exception specifiers
bad_typeidthrown by typeid
bad_function_callthrown by empty function objects
bad_weak_ptrthrown by shared_ptr when passed a bad weak_ptr


Also deriving from exception, header <exception> defines two generic exception types that can be inherited by custom exceptions to report errors:

exceptiondescription
logic_errorerror related to the internal logic of the program
runtime_errorerror detected during runtime

 

Custom Exception

Custom exception can be defined by overriding and inheriting std::exception class functionality.

The following program shows a typical example of custom exception (i.e. user-defined exception) in which exception class is used to define the exception.

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

class CustomException : public exception {
virtual const char* what() const throw() {
return "User-defined Exception";
}
};

int main() {
try {
throw CustomException();
}
catch(exception& ex) {
cout << "CustomException caught" << endl;
cout << ex.what();
}
return 0;
}

The output of the code is given below:

CustomException caught
User-defined Exception