The best programs are written so that computing machines can perform them quickly and so that human beings can understand them clearly. A programmer is ideally an essayist who works with traditional aesthetic and literary forms as
well as mathematical concepts, to communicate the way that an algorithm works and to convince a reader that the results will be correct. Donald E. Knuth

Exception Handling in C++

Exceptions are unusual conditions or problems that arises during the execution of a program like divide by zero, running out of memory or accessing an array out of its bounds. Exception Handling is a mechanism to handle such conditions at run time and prevent abnormal termination or crashing of the program.

Throwing and Catching Exceptions
We can throw an exception from any part of the program using throw keyword whenever an anomalous situation occurs at runtime and then appropriate action is taken after catching the exception. The part of the code which is protected by exception handling is enclosed within try ... catch block. Following program illustrates the use of throw , try and catch keywords :

#include<iostream>
using namespace std;

float divide(float num, float denom) {
   if ( denom == 0 ) {
      throw "Divide by Zero Error";
   }
   else {
      return (num / denom);
   }
}

int main() {
   float x = 5, y = 0;
   /* protect the piece of code using try ... catch block
      and prevent crashing of the program */
   try {
      float result = divide(x, y);
      cout << "Result : " << result << endl;
   } catch (const char *err_msg) {
      cout << "Exception Caught : " << err_msg << endl;
   }
   return 0;
}

In the above program, we have thrown an exception of type const char * and then caught it. We can have multiple catch blocks to catch different types of exception. C++ has a built in class called exception which handles common exception types. In the following program, we try to allocate an array of abnormally large size which leads to bad allocation exception being thrown implicitly. We can catch this type of standard exception using objects of classes which are inherited from exception class.

#include<iostream>
using namespace std;

void func(int size) {
   int *arr = new int[size];
   int i;
   for (i = 0; i < size; i++) {
      arr[i] = i;
   }
}

int main() {
   int size = 1000000000000000000; // abnormally large size
   try {
      cout << "Calling func() " << endl;
      func(size);
   } catch (bad_exception &e) {
      cout << "Exception found : " << e.what() << endl;
   } catch (bad_alloc &e) {
      cout << "Exception Caught : " << e.what() << endl;
   } catch (...) { // default exception
      cout << "Unknown exception caught";
   }
   return 0;
}

In the above program, we have defined multiple catch blocks to handle possible exception cases. In this cases, bad allocation exception is raised which is caught by second catch block using an object of class bad_alloc which is derived from exception class. We can see the error message using the member function what( ). If we are not sure about the class which can catch a specific exception, then we can use an object of exception class itself.

Define New Exception class
Just like the built-in classes bad_alloc and bad_exception , we can define our own exception class to handle new type of exception. In the following program, we define a new exception class and then throw and catch that exception.

#include<iostream>
using namespace std;

// define a new exception inheriting from the base class 
class new_exception : public exception {
public :
   const char *what() const throw() {
      return "New exception found";
   }
};

int main() {
   try {
      throw new_exception();
   } catch (new_exception &e) {
      cout << "Caught : " << e.what() << endl;
   } catch (...) { // default exception
      cout << "Other exception found" << endl;
   }
   return 0;
}


Stack Unwinding
Whenever an exception happens, the control flows to the exception handler and in the process, all the function entries from the stack is removed before the control reaches the function which handles the raised exception. If new objects are constructed during the execution of the program, then the destructors of those objects (constructed since the beginning of try ... catch block ) are called automatically. Following program illustrates the stack unwinding process :

#include<iostream>
using namespace std;

void func1() {
   throw "Out of Memory Exception"; // throw an exception
}

void func2() {
   func1(); // calling function func1()
}

int main() {
   try {
      func2();
   } catch (const char *err_msg) {
      cout << "Exception Caught : " << err_msg << endl;
   }
   return 0;
}

In the above program, main( ) calls func2( ) which in turn calls func1( ). An exception is thrown in func1( ) which flows to main( ) where it is handled and in the process func2( ) and func1( ) are removed from stack. This is stack unwinding. If another exception occurs during unwinding process, then the program ends abnormally after calling function terminate( ).

Re-throwing an Exception
After catching an exception, it can be re-thrown so that another function in the call stack handles it. Following program illustrates this behaviour :

#include<iostream>
using namespace std;

void func1() {
   throw "Out of Memory Exception"; // throw an exception
}

void func2() {
   try {
      func1(); // calling function func1()
   } catch (const char *msg) {
      cout << "Exception caught in func2() ... Rethrowing it" << endl;
      throw msg;
   }
}

int main() {
   try {
      func2();
   } catch (const char *err_msg) {
      cout << "Exception Caught in main() : " << err_msg << endl;
   }
   return 0;
}

In the above program, func2( ) catches the exception thrown in func1( ) and then re-throws that exception which is caught in function main( ).

Back | Next