Introduction To Exceptions
Introduction
Exception handling is a language construct for handling the occurrence of exceptions, special conditions that change the normal flow of program execution.
When return codes are not enough
- Return codes can be cryptic: It's often hard to understand what a return value is.
- Functions can only return one code.
- In sequences of code where many things can go wrong, error codes have to be checked constantly.
- Constructors cannot return codes
- When an error code is returned to the caller, the caller may not always be quipped to handle the error.
Throwing Exceptions
- A throw statement is used to signal that an exception or error case has occurred. It is also known as raising an exception.
Looking and Handling for Exceptions
- In C++, we use the try keyword to define a block of statements.
- The catch keyword is used to define a block of code.
Example
int main() { try { // Statements that may throw exceptions you want to handle now go here throw -1; } catch (int) { // Any exceptions of type int thrown within the above try block get sent here cerr << "We caught an exception of type int" << endl; } catch (double) { // Any exceptions of type double thrown within the above try block get sent here cerr << "We caught an exception of type double" << endl; } return 0; }
Examples
Example 1: How exceptions are handled immediately.
int main() { try { throw 4.5; // throw exception of type double cout << "This never prints" << endl; } catch(double dX) // handle exception of type double { cerr << "We caught a double of value: " << dX << endl; } }
Example 2: Calculating the square root of a number.
#include "math.h" // for sqrt() function using namespace std; int main() { cout << "Enter a number: "; double dX; cin >> dX; try // Look for exceptions that occur within try block and route to attached catch block(s) { // If the user entered a negative number, this is an error condition if (dX < 0.0) throw "Can not take sqrt of negative number"; // throw exception of type char* // Otherwise, print the answer cout << "The sqrt of " << dX << " is " << sqrt(dX) << endl; } catch (char* strException) // catch exceptions of type char* { cerr << "Error: " << strException << endl; } }
Example 3: Multiple statements within a try block
Instead of doing this…
std::ifstream fSetupIni("setup.ini"); // open setup.ini for reading if (!fSetupIni) return ERROR_OPENING_FILE; // Some enum value indicating error // Note that error handling and actual code logic are intermingled if (!ReadParameter(fSetupIni, m_nFirstParameter)) return ERROR_PARAMETER_MISSING; // Some other enum value indicating error if (!ReadParameter(fSetupIni, m_nSecondParameter)) return ERROR_PARAMETER_MISSING; // Some other enum value indicating error if (!ReadParameter(fSetupIni, m_nThirdParameter)) return ERROR_PARAMETER_MISSING; // Some other enum value indicating error
It's better to use exceptions.
std::ifstream fSetupIni("setup.ini"); // open setup.ini for reading if (!fSetupIni) return ERROR_OPENING_FILE; // Some enum value indicating error // Read parameters and return an error if the parameter is missing try { // Here's all the normal code logic m_nFirstParameter = ReadParameter(fSetupIni); m_nSecondParameter = ReadParameter(fSetupIni); m_nThirdParameter = ReadParameter(fSetupIni); } catch (int) // Here's the error handling, nicely separated { return ERROR_PARAMETER_MISSING; // Some other enum value indicating error }
**Example 4: Rewriting the square root program.
#include "math.h" // for sqrt() function using namespace std; // A modular square root function double MySqrt(double dX) { // If the user entered a negative number, this is an error condition if (dX < 0.0) throw "Can not take sqrt of negative number"; // throw exception of type char* return sqrt(dX); } int main() { cout << "Enter a number: "; double dX; cin >> dX; try // Look for exceptions that occur within try block and route to attached catch block(s) { cout << "The sqrt of " << dX << " is " << MySqrt(dX) << endl; } catch (char* strException) // catch exceptions of type char* { cerr << "Error: " << strException << endl; } }
Example 5: Demonstrating stack unwinding
#include <iostream> using namespace std; void Last() // called by Third() { cout << "Start Last" << endl; cout << "Last throwing int exception" << endl; throw -1; cout << "End Last" << endl; } void Third() // called by Second() { cout << "Start Third" << endl; Last(); cout << "End Third" << endl; } void Second() // called by First() { cout << "Start Second" << endl; try { Third(); } catch(double) { cerr << "Second caught double exception" << endl; } cout << "End Second" << endl; } void First() // called by main() { cout << "Start First" << endl; try { Second(); } catch (int) { cerr << "First caught int exception" << endl; } catch (double) { cerr << "First caught double exception" << endl; } cout << "End First" << endl; } int main() { cout << "Start main" << endl; try { First(); } catch (int) { cerr << "main caught int exception" << endl; } cout << "End main" << endl; return 0; }