Exception Handling

Exception Handling
Exception handling is a construct designed to handle the occurrence of exceptions, that is special conditions that changes the normal flow of program execution. Since when designing a programming task (a class or even a function), one cannot always assume that application/task will run or be completed correctly (exit with the result it was intended to). It may be the case that it will be just inappropriate for that given task to report an error message (return an error code) or just exit. To handle these types of cases, C++ supports the use of language constructs to separate error handling and reporting code from ordinary code, that is, constructs that can deal with these exceptions (errors and abnormalities) and so we call this global approach that adds uniformity to program design the exception handling.
Exception safety
A piece of code is said to be exception-safe, if run-time failures within the code will not produce ill effects, such as memory leaks, garbled stored data, or invalid output. Exception-safe code must satisfy invariants placed on the code even if exceptions occur. There are several levels of exception safety:
1. Failure transparency, also known as the no throw guarantee: Operations are guaranteed to succeed and satisfy all requirements even in presence of exceptional situations. If an exception occurs, it will not throw the exception further up. (Best level of exception safety.)
2. Commit or rollback semantics, also known as strong exception safety or no-change guarantee: Operations can fail, but failed operations are guaranteed to have no side effects so all data retain original values.
3. Basic exception safety: Partial execution of failed operations can cause side effects, but invariants on the state are preserved. Any stored data will contain valid values even if data has different values now from before the exception.
4. Minimal exception safety also known as no-leak guarantee: Partial execution of failed operations may store invalid data but will not cause a crash, and no resources get leaked.
5. No exception safety: No guarantees are made. (Worst level of exception safety)
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.
1. throw: A program throws an exception when a problem shows up. This is done using a throw keyword.There are two main ways an abnormal program behavior is transferred from the try block to the catch clause. This transfer is actually carried by the throw keyword. Unlike the try and catch blocks, the throw keyword is independent of a formal syntax but still follows some rules.
2. 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.
try {Behavior}
The try keyword is required. It lets the compiler know that you are anticipating an abnormal behavior and will try to deal with it. The actual behavior that needs to be evaluated is included between an opening curly bracket “{“ and a closing curly bracket “}”. Inside of the brackets, implement the normal flow that the program should follow, at least for this section of the code.
3. catch: During the flow of the program as part of the try section, if an abnormal behavior occurs, instead of letting the program crash or instead of letting the compiler send the error to the operating system, you can transfer the flow of the program to another section that can deal with it. The syntax used by this section is:
catch(Argument) {WhatToDo}
This section always follows the try section and there must not be any code between the try’s closing bracket and the catch section. The catch keyword is required and follows the try section. The catch behaves a little like a function. It uses an argument that is passed by the previous try section. The argument can be a regular variable or a class. If there is no argument to pass, the catch must at least take a three-period argument as in catch(…). The behavior of the catch clause starts with an opening curly bracket “{“ and ends with a closing curly bracket “}”. The inside of the brackets is called the body of the catch clause. Therefore, use the body of the catch to deal with the error that was caused.
Combined with the try block, the syntax of an exception would be:
try {
// Try the program flow
}
catch(Argument)
{

// Catch the exception

}

Exception handling is a mechanism that separates code that detects and handles exceptional circumstances from the rest of your program. Note that an exceptional circumstance is not necessarily an error.

When a function detects an exceptional situation, you represent this with an object. This object is called an exception object. In order to deal with the exceptional situation you throw the exception. This passes control, as well as the exception, to a designated block of code in a direct or indirect caller of the function that threw the exception. This block of code is called a handler. In a handler, you specify the types of exceptions that it may process. The C++ run time, together with the generated code, will pass control to the first appropriate handler that is able to process the exception thrown. When this happens, an exception is caught. A handler may rethrow an exception so it can be caught by another handler.
Whenever an exception occurs, and whenever you use the try keyword to try an expression, you must transfer control to a catch block. This is where you should display your own message for the error. Here is an example: -
#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;
}
Output : -
Division by zero condition!
C++ Standard Exceptions: -
C++ provides a list of standard exceptions defined in 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:-
Exception Description
std::exception An exception and parent class of all the standard C++ exceptions.
std::bad_alloc This can be thrown by new.
std::bad_cast This can be thrown by dynamic_cast.
std::bad_exception This is useful device to handle unexpected exceptions in a C++ program
std::bad_typeid This can be thrown by typeid.
std::logic_error An exception that theoretically can be detected by reading the code.
std::domain_error This is an exception thrown when a mathematically invalid domain is used
std::invalid_argument This is thrown due to invalid arguments.
std::length_error This is thrown when a too big std::string is created
std::out_of_range This can be thrown by the at method from for example a std::vector and std::bitset<>::operator[]().
std::runtime_error An exception that theoretically can not be detected by reading the code.
std::overflow_error This is thrown if a mathematical overflow occurs.
std::range_error This is occured when you try to store a value which is out of range.
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: -
Example: -
#include
#include
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
}
}
Output: -
MyException caught
C++ Exception
The compiler would proceed in a top-down as follows:
1. Following the normal flow control of the program, the compiler enters the try block.
2. If no exception occurs in the try block, the rest of the try block is executed.
If an exception occurs in the try block, the try displays a throw that specifies the type of error that happened.
a). The compiler gets out of the try block and examines the first catch
b). If the first catch doesn’t match the thrown error, the compiler proceeds with the next catch. This continues until the compiler finds a catch that matches the thrown error.
c). If one of the catches matches the thrown error, its body executes. If no catch matches the thrown error, you have (or the compiler has) two alternatives. If there is no catch that matches the error (which means that you didn’t provide a matching catch), the compiler hands the program flow to the operating system (which calls the terminate() function). Another alternative is to include a catch whose argument is three periods: catch(…). The catch(…) is used if no other catch, provided there was another, matches the thrown error. The catch(…), if included as part of a catch clause, must always be the last catch, unless it is the only catch of the clause.
Nesting Exceptions
C++ allows you to nest exceptions, using the same techniques we applied to nest conditional statements. This means that you can write an exception that depends on, and is subject to, another exception. To nest an exception, write a try block in the body of the parent exception. The nested try block must be followed by its own catch(es). To effectively handle the exception, make sure you include an appropriate throw in the try block. Here is an exception:
#include < iostream >
#include < string >
using namespace std;
int main()
{
char Number1[40], Number2[40];
double Operand1, Operand2, Result;
char Operator;
cout << "This program allows you to perform an operation on two numbers\n";
try {
cout << "To proceed, enter\n";
cout << "First Number: "; cin >> Number1;
cout << "An Operator: "; cin >> Operator;
cout << "Second Number: "; cin >> Number2;
// Examine each character of the first operand
// to find out if the user included a non-digit in the number
for(int i = 0; i < strlen(Number1); i++)
if( (!isdigit(Number1[i])) && (Number1[i] != '.') ) // Allow the period throw Number1; // Send the error as a character
Operand1 = atof(Number1);
// Do the same for the second number entered
for(int j = 0; j < strlen(Number2); j++)
if( (!isdigit(Number2[j])) && (Number2[j] != '.') ) // Allow the period throw Number2;//[j]; // Send the error as a character
Operand2 = atof(Number2);
if(Operator != '+' && Operator != '-' &&
Operator != '*' && Operator != '/')
throw Operator;
switch(Operator)
{
case '+':
Result = Operand1 + Operand2;
cout << "\n" << Operand1 << " +
" << Operand2 << " = " << Result;
break;
case '-':
Result = Operand1 - Operand2;
cout << "\n" << Operand1 << " - "
<< Operand2 << " = " << Result;
break;
case '*':
Result = Operand1 * Operand2;
cout << "\n" << Operand1 << " * "
<< Operand2 << " = " << Result;
break;
case '/': // The following exception is nested in the previous try
try {
if(Operand2 == 0)
throw "Division by 0 not allowed";
Result = Operand1 / Operand2;
cout << "\n" << Operand1 << " / "
<< Operand2 << " = " << Result;
}
catch(const char * Str)
{
cout << "\nBad Operation: " << Str;
}
break;
}
}
catch(const char n)
{
cout << "\nOperation Error: " << n << " is not a valid operator";
}
catch(const char *BadOperand)
{
cout << "\nError: " << BadOperand << " is not a valid number";
}
cout << "\n\n";
return 0;
}
Exceptions and Functions
One of the most effective techniques used to deal with code is to isolate assignments. We have learned this when studying functions. For example, the switch statement that was performing the operations in the “normal” version of our program can be written as follows: -
#include < iostream >
#include < string >
using namespace std;
void Calculator(const double N1, const double N2, const char p) throw(const char*, const char);
double Validate(const char *N) throw(const char*);
int main()
{
char Number1[40], Number2[40];
double Operand1, Operand2;
char Operator;
cout << "This program allows you to perform an operation on two numbers\n";
try {
cout << "To proceed, enter\n";
cout << "First Number: "; cin >> Number1;
cout << "An Operator: "; cin >> Operator;
cout << "Second Number: "; cin >> Number2;
Operand1 = Validate(Number1);
Operand2 = Validate(Number2);
try {
Calculator(Operand1, Operand2, Operator);
}
catch(const char * Str)
{
cout << "\nBad Operation: " << Str;
}
}
catch(const char n)
{
cout << "\nOperation Error: " << n << " is not a valid operator";
}
catch(const char *BadOperand)
{
cout << "\nError: " << BadOperand << " is not a valid number";
}
cout << "\n\n";
return 0;
}
void Calculator(const double Oper1, const double Oper2, const char Symbol) throw(const char*, const char)
{
double Value;
if(Symbol != '+' && Symbol != '-' && Symbol != '*' && Symbol != '/')
throw Symbol;
switch(Symbol)
{
case '+':
Value = Oper1 + Oper2;
cout << "\n" << Oper1 << " + " << Oper2 << " = " << Value;
break;
case '-':
Value = Oper1 - Oper2;
cout << "\n" << Oper1 << " - " << Oper2 << " = " << Value;
break;
case '*':
Value = Oper1 * Oper2;
cout << "\n" << Oper1 << " * " << Oper2 << " = " << Value;
break;
case '/':
if(Oper2 == 0)
throw "Division by 0 not allowed";
Value = Oper1 / Oper2;
cout << "\n" << Oper1 << " / " << Oper2 << " = " << Value;
break;
}
}
double Validate(const char* N) throw(const char*)
{
double Valid;
for(int i = 0; i < strlen(N); i++)
if( (!isdigit(N[i])) && (N[i] != '.') )
throw N;
Valid = atof(N);
return Valid;
}
Exception specifications
The range of exceptions that can be thrown by a function are an important part of that function's public interface. Without this information, you would have to assume that any exception could occur when calling any function, and consequently write code that was extremely defensive. Knowing the list of exceptions that can be thrown, you can simplify your code since it doesn't need to handle every case.
This exception information is specifically part of the public interface. Users of a class don't need to know anything about the way it is implemented, but they do need to know about the exceptions that can be thrown, just as they need to know the number and type of parameters to a member function. One way of providing this information to clients of a library is via code documentation, but this needs to be manually updated very carefully. Incorrect exception information is worse than none at all, since you may end up writing code that is less exception-safe than you intended to.
C++ provides another way of recording the exception interface, by means of exception specifications. An exception specification is parsed by the compiler, which provides a measure of automated checking. An exception specification can be applied to any function, and looks like this:
double divide(double dNumerator, double dDenominator) throw (DivideByZeroException);
Example: -
void DubiousFunction(int iFoo) throw()
{
if (iFoo < 0)
{
throw RangeException();
}
}
If an exception is thrown at run time that propagates out of a function that doesn't allow the exception in its exception specification, the exception will not propagate any further and instead, the function RangeException() will be called. The RangeException() function doesn't return, but can throw a different type of exception that may (or may not) satisfy the exception specification and allow exception handling to carry on normally. If this still doesn't recover the situation, the program will be terminated.


Free Web Hosting