Table of Contents
What is an exception?
Exception is an event that happens when unexpected circumstances appear. It can be a runtime error or you can create an exceptional situation programmatically.
Exception handling consists in transferring control from the place where exception happened to the special functions (commands) called handlers.
How to handle exceptions?
Exceptions are handled by using try/catch block. The code that can produce an exception is surrounded with try block. The handler for this exception is placed in catch block:
Try/catch block uses the following syntax:
try { //code that can produce error } catch (ExceptionType e) { //exception handler }
Example of an exception:
Look on the following example:
//use a vector vector<int> arr; //push two elements into vector arr.push_back(4); arr.push_back(7); //try to access the third element that does not exist arr.at(2);
This code compiles without errors. Nevertheless, if you try to run this program, you will get an error:
You are trying to access an element of a vector that does not exist. at(int) member function of vector throws exception when it is called.
Example of exception handling.
You can re-write this program using exception-handling mechanism:
//use a vector vector<int> arr; //push two elements into vector arr.push_back(4); arr.push_back(7); //try to access the third element that does not exist try { arr.at(2); } catch (...)//catch all exceptions { cout << "Exception happened" << endl; }
As you can see, exception handling makes your code safer and protects your program from runtime errors.
Built-in exceptions
C++ provides a range of built in exceptions. The base class for all exceptions classes is exception
The information about happened exception is provided by what() member function of the exception class:
//try to access the third element that does not exist try { arr.at(2); } catch (exception& e)//catch all exceptions { cout << "Exception happened: " << e.what() << endl; }
In this case, you will get following output:
Exception happened: invalid vector<T> subscript
All the Standard Exceptions are listed in the following diagram:
Short description of these exceptions is provided in these tables:
Exceptions derived directly from exception class:
bad_alloc | Happens when there is a failure of memory allocation |
bad_cast | Is thrown when dynamic_cast is used incorrect |
bad_exception | Exception that is thrown by unexpected handler |
bad_function_call | Thrown when an empty (not implemented) function is called |
bad_typeid | Thrown by typeid function |
bad_weak_ptr | Exception that can be thrown by shared_ptr class constructor |
ios_base::failure | Base class for all the stream exceptions |
logic_error | Base class for some logic error exceptions |
runtime_error | Base class for some runtime error exceptions |
Exceptions derived indirectly from exception class through logic_error:
domain_error | Thrown when an error of function domain happens |
future_error | Reports an exception that can happen in future objects(See more info about future class) |
invalid_argument | Exception is thrown when invalid argument is passed |
length_error | Is thrown when incorrect length is set |
out_of range error | Is thrown when out of range index is used |
Exceptions derived indirectly from exception class through runtime_error:
overflow_error | Arithmetic overflow exception |
range_error | Signals range error in computations |
system_error | Reports an exception from operating system |
underflow_error | Arithmetic underflow exceptions |
How to throw an exception
There is a possibility to throw an exception. It can be done by using throw keyword.
Look on example:
double divide(double a, double b) { if (b == 0)//division by zero!!!! throw "Division by zero"; else return a / b; }
Function divide throws an exception in form of string, when denominator is zero. In this case, the execution of function is terminated and exception is thrown to the place where function was called. Now, try to call this function with incorrect parameter:
try { double res = divide(1, 0); } catch (char* c) { cout << c; }
This produces the following output:
Division by zero
How to extend exception class?
Sometimes, you will need to create your own exception classes. This can be done for different purposes. For example, you want to send some information from the place, where exception happened, to the catch block. It can be done by extending class exception. In this case, you can create a constructor for the derived class and override its member function what().
class ZeroDivisionException :public exception { public: ZeroDivisionException(int data) { someData = data; } //override what function const char* what() { return "Zero division error"; } int someData; };
ZeroDivisionException class stores an integer data member someData. Now we can get the information from the place, where exception happened, in the catch block. For this purpose, we will re-write divide function:
double divide(double a, double b) { if (b == 0) //division by zero!!!! throw ZeroDivisionException(a); else return a/b; }
Parameter a is passed and returned back from the function. We can use it in catch block:
try { double res = divide(1, 0); } catch (ZeroDivisionException e) { cout << e.what() << endl; cout << "Trying to divide " << e.someData << " by zero" << endl; }
The output is
Zero division error
Trying to divide 1 by zero
Exceptions provide a powerful and efficient mechanism to control errors and make your programs safer.