UNIT 8
Exception Handling
An exception is a problem that arises during the execution of a program. A C++ exception is a response to an exceptional circumstance that arises while a program is running, such as an attempt to divide by zero.
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.
- Throw − A program throws an exception when a problem shows up. This is done using a throw keyword.
- Catch − A program catches an exception with an exception handler at the place in a program where you want to handle the problem. The catch keyword indicates the catching of an exception.
- Try − A try block identifies a block of code for which particular exceptions will be activated. It's followed by one or more catch blocks.
Assuming a block will raise an exception, a method catches an exception using a combination of the try and catch keywords. A try/catch block is placed around the code that might generate an exception. Code within a try/catch block is referred to as protected code, and the syntax for using try/catch as follows −
Try {
// protected code
} catch( ExceptionName e1 ) {
// catch block
} catch( ExceptionName e2 ) {
// catch block
} catch( ExceptionName eN ) {
// catch block
}
You can list down multiple catch statements to catch different type of exceptions in case your try block raises more than one exception in different situations.
Throwing Exceptions
Exceptions can be thrown anywhere within a code block using throw statement. The operand of the throw statement determines a type for the exception and can be any expression and the type of the result of the expression determines the type of exception thrown.
Following is an example of throwing an exception when dividing by zero condition occurs −
Double division(int a, int b) {
If( b == 0 ) {
Throw "Division by zero condition!";
}
Return (a/b);
}
Catching Exceptions
The catch block following the try block catches any exception. You can specify what type of exception you want to catch and this is determined by the exception declaration that appears in parentheses following the keyword catch.
Try {
// protected code
} catch( ExceptionName e ) {
// code to handle ExceptionName exception
}
Above code will catch an exception of ExceptionName type. If you want to specify that a catch block should handle any type of exception that is thrown in a try block, you must put an ellipsis, ..., between the parentheses enclosing the exception declaration as follows −
Try {
// protected code
} catch(...) {
// code to handle any exception
}
The following is an example, which throws a division by zero exception and we catch it in catch block.
#include <iostream>
Using namespace std;
Double division(int a, int b) {
If( b == 0 ) {
Throw "Division by zero condition!";
}
Return (a/b);
}
Int main () {
Int x = 50;
Int y = 0;
Double z = 0;
Try {
z = division(x, y);
Cout << z << endl;
} catch (const char* msg) {
Cerr << msg << endl;
}
Return 0;
}
Because we are raising an exception of type const char*, so while catching this exception, we have to use const char* in catch block. If we compile and run above code, this would produce the following result −
Division by zero condition!
C++ Standard Exceptions
C++ provides a list of standard exceptions defined in <exception> which we can use in our programs. These are arranged in a parent-child class hierarchy shown below −
Here is the small description of each exception mentioned in the above hierarchy −
Sr.No | Exception & Description |
1 | Std::exception An exception and parent class of all the standard C++ exceptions. |
2 | Std::bad_alloc This can be thrown by new. |
3 | Std::bad_cast This can be thrown by dynamic_cast. |
4 | Std::bad_exception This is useful device to handle unexpected exceptions in a C++ program. |
5 | Std::bad_typeid This can be thrown by typeid. |
6 | Std::logic_error An exception that theoretically can be detected by reading the code. |
7 | Std::domain_error This is an exception thrown when a mathematically invalid domain is used. |
8 | Std::invalid_argument This is thrown due to invalid arguments. |
9 | Std::length_error This is thrown when a too big std::string is created. |
10 | Std::out_of_range This can be thrown by the 'at' method, for example a std::vector and std::bitset<>::operator[](). |
11 | Std::runtime_error An exception that theoretically cannot be detected by reading the code. |
12 | Std::overflow_error This is thrown if a mathematical overflow occurs. |
13 | Std::range_error This is occurred when you try to store a value which is out of range. |
14 | Std::underflow_error This is thrown if a mathematical underflow occurs. |
Define New Exceptions
You can define your own exceptions by inheriting and overriding exception class functionality. Following is the example, which shows how you can use std::exception class to implement your own exception in standard way −
#include <iostream>
#include <exception>
Using namespace std;
Struct MyException : public exception {
Const char * what () const throw () {
Return "C++ Exception";
}
};
Int main() {
Try {
Throw MyException();
} catch(MyException& e) {
Std::cout << "MyException caught" << std::endl;
Std::cout << e.what() << std::endl;
} catch(std::exception& e) {
//Other errors
}
}
This would produce the following result −
MyException caught
C++ Exception
Here, what() is a public method provided by exception class and it has been overridden by all the child exception classes. This returns the cause of an exception.
The new exception can be defined by overriding and inheriting exception class functionality.
C++ user-defined exception example
Let's see the simple example of user-defined exception in which std::exception class is used to define the exception.
- #include <iostream>
- #include <exception>
- Using namespace std;
- Class MyException : public exception{
- Public:
- Const char * what() const throw()
- {
- Return "Attempted to divide by zero!\n";
- }
- };
- Int main()
- {
- Try
- {
- Int x, y;
- Cout << "Enter the two numbers : \n";
- Cin >> x >> y;
- If (y == 0)
- {
- MyException z;
- Throw z;
- }
- Else
- {
- Cout << "x / y = " << x/y << endl;
- }
- }
- Catch(exception& e)
- {
- Cout << e.what();
- }
- }
Output:
Enter the two numbers :
10
2
x / y = 5
Output:
Enter the two numbers :
10
0
Attempted to divide by zero!
-->
Note: In above example what() is a public method provided by the exception class. It is used to return the cause of an exception.
One of the advantages of C++ over C is Exception Handling. Exceptions are run-time anomalies or abnormal conditions that a program encounters during its execution. There are two types of exceptions: a)Synchronous, b)Asynchronous(Ex:which are beyond the program’s control, Disc failure etc). C++ provides following specialized keywords for this purpose.
Try: represents a block of code that can throw an exception.
Catch: represents a block of code that is executed when a particular exception is thrown.
Throw: Used to throw an exception. Also used to list the exceptions that a function throws, but doesn’t handle itself.
Why Exception Handling?
Following are main advantages of exception handling over traditional error handling.
1) Separation of Error Handling code from Normal Code: In traditional error handling codes, there are always if else conditions to handle errors. These conditions and the code to handle errors get mixed up with the normal flow. This makes the code less readable and maintainable. With try catch blocks, the code for error handling becomes separate from the normal flow.
2) Functions/Methods can handle any exceptions they choose: A function can throw many exceptions, but may choose to handle some of them. The other exceptions which are thrown, but not caught can be handled by caller. If the caller chooses not to catch them, then the exceptions are handled by caller of the caller.
In C++, a function can specify the exceptions that it throws using the throw keyword. The caller of this function must handle the exception in some way (either by specifying it again or catching it)
3) Grouping of Error Types: In C++, both basic types and objects can be thrown as exception. We can create a hierarchy of exception objects, group exceptions in namespaces or classes, categorize them according to types.
Exception Handling in C++
1) Following is a simple example to show exception handling in C++. The output of program explains flow of execution of try/catch blocks.
#include <iostream> Using namespace std;
Int main() { Int x = -1;
// Some code Cout << "Before try \n"; Try { Cout << "Inside try \n"; If (x < 0) { Throw x; Cout << "After throw (Never executed) \n"; } } Catch (int x ) { Cout << "Exception Caught \n"; }
Cout << "After catch (Will be executed) \n"; Return 0; } |
Output:
Before try
Inside try
Exception Caught
After catch (Will be executed)
2) There is a special catch block called ‘catch all’ catch(…) that can be used to catch all types of exceptions. For example, in the following program, an int is thrown as an exception, but there is no catch block for int, so catch(…) block will be executed.
#include <iostream> Using namespace std;
Int main() { Try { Throw 10; } Catch (char *excp) { Cout << "Caught " << excp; } Catch (...) { Cout << "Default Exception\n"; } Return 0; } |
Output:
Default Exception
3) Implicit type conversion doesn’t happen for primitive types. For example, in the following program ‘a’ is not implicitly converted to int
#include <iostream> Using namespace std;
Int main() { Try { Throw 'a'; } Catch (int x) { Cout << "Caught " << x; } Catch (...) { Cout << "Default Exception\n"; } Return 0; } |
Output:
Default Exception
4) If an exception is thrown and not caught anywhere, the program terminates abnormally. For example, in the following program, a char is thrown, but there is no catch block to catch a char.
#include <iostream> Using namespace std;
Int main() { Try { Throw 'a'; } Catch (int x) { Cout << "Caught "; } Return 0; } |
Output:
Terminate called after throwing an instance of 'char'
This application has requested the Runtime to terminate it in an
Unusual way. Please contact the application's support team for
More information.
5) A derived class exception should be caught before a base class exception.
6) Like Java, C++ library has a standard exception class which is base class for all standard exceptions. All objects thrown by components of the standard library are derived from this class. Therefore, all standard exceptions can be caught by catching this type
7) Unlike Java, in C++, all exceptions are unchecked. Compiler doesn’t check whether an exception is caught or not . For example, in C++, it is not necessary to specify all uncaught exceptions in a function declaration. Although it’s a recommended practice to do so. For example, the following program compiles fine, but ideally signature of fun() should list unchecked exceptions.
#include <iostream> Using namespace std;
// This function signature is fine by the compiler, but not recommended. // Ideally, the function should specify all uncaught exceptions and function // signature should be "void fun(int *ptr, int x) throw (int *, int)" Void fun(int *ptr, int x) { If (ptr == NULL) Throw ptr; If (x == 0) Throw x; /* Some functionality */ }
Int main() { Try { Fun(NULL, 0); } Catch(...) { Cout << "Caught exception from fun()"; } Return 0; } |
Output:
Caught exception from fun()
A better way to write above code
#include <iostream> Using namespace std;
// Here we specify the exceptions that this function // throws. Void fun(int *ptr, int x) throw (int *, int)" { If (ptr == NULL) Throw ptr; If (x == 0) Throw x; /* Some functionality */ }
Int main() { Try { Fun(NULL, 0); } Catch(...) { Cout << "Caught exception from fun()"; } Return 0; } |
Output:
Caught exception from fun()
8) In C++, try-catch blocks can be nested. Also, an exception can be re-thrown using “throw; ”
#include <iostream> Using namespace std;
Int main() { Try { Try { Throw 20; } Catch (int n) { Cout << "Handle Partially "; Throw; //Re-throwing an exception } } Catch (int n) { Cout << "Handle remaining "; } Return 0; } |
Output:
Handle Partially Handle remaining
A function can also re-throw a function using same “throw; “. A function can handle a part and can ask the caller to handle remaining.
9) When an exception is thrown, all objects created inside the enclosing try block are destructed before the control is transferred to catch block.
#include <iostream> Using namespace std;
Class Test { Public: Test() { cout << "Constructor of Test " << endl; } ~Test() { cout << "Destructor of Test " << endl; } };
Int main() { Try { Test t1; Throw 10; } catch(int i) { Cout << "Caught " << i << endl; } } |
Output:
Constructor of Test
Destructor of Test
Caught 10
Here we will see what the meaning of stack unwinding is. When we call some functions, it stores the address into call stack, and after coming back from the functions, pops out the address to start the work where it was left of.
The stack unwinding is a process where the function call stack entries are removed at runtime. To remove stack elements, we can use exceptions. If an exception is thrown from the inner function, then all of the entries of the stack is removed, and return to the main invoker function.
Let us see the effect of stack unwinding through an example.
Example Code
#include <iostream>
Using namespace std;
Void function1() throw (int) { //this function throws exception
Cout<<"\n Entering into function 1";
Throw 100;
Cout<<"\n Exiting function 1";
}
Void function2() throw (int) { //This function calls function 1
Cout<<"\n Entering into function 2";
Function1();
Cout<<"\n Exiting function 2";
}
Void function3() { //function to call function2, and handle
Exception thrown by function1
Cout<<"\n Entering function 3 ";
Try {
Function2(); //try to execute function 2
}
Catch(int i) {
Cout<<"\n Caught Exception: "<<i;
}
Cout<<"\n Exiting function 3";
}
Int main() {
Function3();
Return 0;
}
Output
Entering function 3
Entering into function 2
Entering into function 1
Caught Exception: 100
Exiting function 3
Here we can see that the control stores info of function3, then enters into function2, and then into function1. After that one exception occurs, so it removes all of the information from stack, and returns back to function3 again.
Reference Books
1 “Thinking in C++”, Volume 1 and 2 by Bruce Eckel, Chuck Allison, Pearson
Education
2 “Mastering C++”, 1/e by Venugopal, TataMcGraw Hill.
3 “Object Oriented Programming with C++”, 3/e by E. Balaguruswamy, Tata
McGraw Hill.
4 “Starting Out with Object Oriented Programming in C++”, by Tony Gaddis,
Wiley India.