Error Handling
This section also applies to C programs. [Not all covered in Fall 2025.]
exit(int)
- Must
#include <cstdlib> - Used to cleanly and gracefully exit a program immediately
- Will flush buffers and close files, and execute destructors
- Return code
intis passed to the operating system- 0 (but use macro
EXIT_SUCCESS) indicates no errors - non-zero (macro
EXIT_FAILURE) indicates errors - No return to main, immediate quit of program run
- 0 (but use macro
abort()
- End program immediately with no clean-up (no destructors, etc.)
assert(condition)
- Its incredibly important to assert invariants you know should be true -- will find errors earlier
- Must #include <cassert>
- Ignored if macro symbol
NDEBUGis defined - If condition true, nothing happens
- If condition false, program aborts, prints file/line# of error and assertion that failed
Example:
Point *parray;
parray = new Point[6];
if (parray == 0) {
cerr << "not enough memory for point array";
exit(EXIT_FAILURE);
}
assert(parray != 0);
Exceptions
What are exceptions?
- Essentially they are objects that indicate particular error conditions have occurred
- Throwing an exception (via
throwsyntax) causes the normal program flow to be interrupted - At first glance this is like
exitorabort - But, you can catch the exception (via
try ...catchsyntax) and handle it gracefully instead of the whole program stopping - Exceptions thrown include the type of exception and a value of any type that indicates problem details - built-in types include
invalid_argument,underflow_error; see documentation for the full list. - Exceptions are an important programming tool for more structured handling of errors - in C you have to manually keep passing up error codes from failing functions, makes code hard to read
How exceptions are processed at run-time
- Program
throws an exception right where the error first occurs - C++ searches for a
try-catchblock to handle the exception - The
throwcould have syntactically been inside atryblock to quickly handle it - Or (much more common) some calling function up the call stack was running in a
tryblock- Unwinds the stack by destroying objects created since it entered the try block
- Destructors for local objects in function throwing exception are called
- The
try-catchblock stops the exception from propagating up the stack (catches it) and resumes normal execution- It's a bit more complex than that,
catchdeclares a type of exceptions to be caught and only that type is caught - Also, can rethrow (pretend you didn't catch) the exception by simply saying
throw;in the catch block - If the exception is never caught the program will call
std::terminatewhich callsstd::abort.
- It's a bit more complex than that,
Simple example:
#include <iostream>
int bust(int n) throw (std::overflow_error) // declare exception raised in bust
{
std::cout << "calling function bust(" << n << ")" << std::endl;
if (n >100000) throw std::overflow_error("buuusted!");
std::cout << "finishing function bust" << std::endl;
return 0;
};
void must(int n) throw (std::overflow_error) // since must does not catch, bust exception also declared here
{
bust(n);
std::cout << "finishing running must, bust did not throw exception" << std::endl;
};
int main ()
{
try
{ must(33); }
catch (std::exception & e)
{ std::cout << "caught exception HERE!" << std::endl; } // doesn't run
try
{ must(8723643); }
catch (std::exception & e)
{ std::cout << "caught exception THERE!" << std::endl; }
std::cout << "program still keeps running since exception caught" << std::endl;
}
Options for declaring in the header which exceptions a function can throw
void bust(int n) throw (std::overflow_error)meansbustcan throw and perhaps not internally catchstd::overflow_error- and,
bustcannot throw any other exceptions
void bust(int n) throw()meansbustcan't throw any exceptionsvoid bust(int n)-- if there is nothrowin the header, any exception can be thrown
What C++ does with these declarations
- Nothing is checked at compile time (unlike Java exception declarations)
- If an unlisted exception is thrown,
std::unexpectedis called at run-time - If there is no
throwlist (as opposed tothrow ()),std::unexpectedwill not be called
Making your own exception objects
- The system exceptions such as
std::underflow_errorare in fact classes - Throwing an exception causes the run-time to make an object of the class
- The object records relevant information about the exception
- Many system exceptions are subclasses of library class
exception(#include <exception>) - You can make your own sort of exception by making your own subclass of
exception - Exception type can in fact be anything (
int,char, etc) but not generally recommended, subclass fromexceptioninstead
An example of a user-declared exception type:
#include <iostream>
#include <exception>
class boom: public std::exception {
virtual const char* what() const throw() // override what() to customize error string
{ return "BOOM!"; }
};
int main ()
{
boom myex;
try
{ throw myex; }
catch (std::exception & e)
{
std::cout << "Exception what code is: " << e.what() << std::endl;
}
}
Observe about the above:
- Every exception object has a message string
(const char *),e.what() - Constructors for
exceptionobjects have string argument (to specify the message) - Catch parameters must be passed by reference to take advantage of inheritance