An exception is raised when the event occurs, the special processing is exception handling, and the routine which carries this out is the exception handler.
There are a wide variety of exception types, from hardware-detectable errors such as divide-by-zero, to file handling exceptions such as end-of-file, to subscript-checking exceptions (e.g. out-of-bounds array accesses).
Different exception handling facilities are provided in different languages - where a particular ability is not directly provided by the language it is the user's responsibility to devise their own handling for the event in question.
For example, the compiler for a particular language might automatically insert checks for array bounds or divide-by-zero cases: this results in a simpler and more readable user program since the user source code does not need to explicitly include the checks and handlers.
For any programming language we use, there are a number of factors we need to know:
try
blocks
which define the scope of exceptions, and a subsequent sequence
of catch
blocks which handle exceptions generated
in the preceding try block. I.e.
try { ... // normal processing ... if (/* SOME ERROR CONDITION IS DETECTED */) { throw exception_class_type(actual parameters); // this creates an exception object, gives it some parameter data, // and throws the object out to be (hopefully) caught and // processed by an exception handler somewhere } ... // otherwise normal processing continues ... } catch (formal parameter exception class type 1) // handler 1, processes exceptions which are of class type 1 // or which are derived from this class { // handle the exception if (/* further exception handling deemed necessary */) throw; // re-throw unhandled exceptions } catch (formal parameter exception class type 2) { // handler 2, processes unhandled exceptions which are of // class type 2, or which are derived from this class } ...When an exception is generated (thrown) the try block it occurs in is immediately terminated, and the "catch" statements following the block are sequentially searched.
The first catch whose parameter types match the thrown exception, is used for exception handling.
If a block is unable to handle the exception it can throw it again, hopefully to be caught by a subsequent catch block.
In general:
int fun() throw (int, char *) { ... }
(by default any function can throw any exception type)
An example of exception handling:
In this example (modified from R. Sebesta's text "Concepts of Programming Languages") try and catch are used in reading and handling a list of grades.
#include <iostream> class endofdata { public: endofdata() { } }; class badgrade { public: badgrade(int g) { grade = g; } int getinput() { return grade; } protected: int grade; }; int main() // here we read in a list of grades and count how many fall // into each range: 0-9, 10-19, etc. // exception handling is used to detect // the end of input and invalid grades (out of range) { int new_grade; // records a newly read grade int index; // index into the array of grade distribution int lower_limit; // lower bound on a grade subrange int upper_limit; // upper bound on a grade subrange int distribution[10] = // record distribution of grades { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; short int endofdata = 0; // flag to record the end of input // the overall try block reads the grades try { while (1) { // do forever, using the exception generator // to break out of the infinite loop when // the end of input data is reached // throw an exception at the end of input data if (!cin >> new_grade) throw endofdata; // since we haven't hit the end of the data yet, // process the next grade { // the inner try block checks for out-of-range grades try { // throw an exception on an out-of-range grade if ((new_grade < 0) || (new_grade > 100)) throw badgrade(new_grade); // otherwise process the grade index = new_grade / 10; if (index == 10) index = 9; distribution[index] += 1; } // the inner catch block prints an error message // for out-of-range grades catch (badgrade e) { cout << "Error: grade " << e.getgrade() << " is invalid" << endl; } } } } // the overall catch block handles the end of data, // displaying the grade distribution catch(endofdata e) { cout << "Range Frequency" << endl; for (int i = 0; i < 10; i++) { lower_limit = i * 10; if (i == 9) upper_limit = 100; else upper_limit = lower_limit + 9; cout << lower_limit << "-" << upper_limit << " "; cout << distribution[i] << endl; } } }
Generally accepted guidelines for the use of exceptions (as opposed to "normal" data handling) include:
The base class for the heirarchy is exception, so to ensure safety we nest the contents of our main routine in a try block, and follow that with a general catch for the base exception type.
This guarantees that any unhandled (non-global) exception will be caught as long as we only ever throw descendants of the base exception class.
#include <iostream> #include <stdexcept> using namespace std; void broken() { // lets just throw an exception throw runtime_error("this is a runtime error"); } int main() { try { // call a function which will generate, but not handle, // a standard exception broken(); } catch (std::exception& e) { // catches any unhandled standard exceptions // (or exception derived from them) cout << "Caught an unhandled exception: " << e.what() << endl; } }For more information on this library try man exception.