PRG355 - Lecture - 12 - Exception Handling

Download as pdf or txt
Download as pdf or txt
You are on page 1of 32

PRG355 Lecture 12:

Exception Handling
Adam Arruda
Agenda
• Fundamentals of Exception Handling
• Using try-throw-catch
• Throwing and Handling an Exception
• Exception Specifications
• Handling Memory Allocation Errors
• Exceptions and Classes
Exception Handling
• An exception is an occurrence that causes abnormal program termination.
• These occurrences may be caused by hardware failures (e.g. insufficient
resources, or errors in peripheral devices) or by the user (garbage data
input).
• When designing programs used in critical applications such as nuclear
power plants, medical devices, or aircraft guidance systems, it is important
that these programs are robust and not prone to errors.
• Programs must therefore efficiently process (handle) exceptions.
Exception Handling
• An exception can be also defined as an object that is thrown at the location
in a program where a run-time error occurs.
• This object must be caught and handled by code called the exception
handler, which is designed to process that particular type of run-time error.
• When processing exceptions, the following two tasks must be successfully
completed:
• Detecting and throwing (raising) an exception
• Handling an exception
• By handling an exception a program can potentially recover from an error
situation
• If the error is not possible to recover from, it allows you to gracefully
terminate a program without abruptly crashing
Exception Handling
• The following run-time errors are common examples of exceptions that may
cause abnormal program termination.
• a division by 0
• an even root of a negative number
• insufficient memory in the heap
• errors in peripheral devices
• If an exception is detected during program execution, it is thrown to the code
(exception handler) that handles that particular type of exception.
• Programmers should anticipate various kinds of exceptions that may interrupt
their programs at execution time and design code that detects, throws, and
handles these exceptions.
• Programs that include the exception-handling code are more robust, less error-
prone, and less likely to crash than programs without this code.
Exception Handling
• Programming languages that do not provide C++ exception-handling tools
use techniques that perform error checking and error handling at the
locations in the code where errors may occur during program execution.
• The disadvantage of this approach is that program readability may be
decreased due to the interspersing of error-checking and handling code
within the main code.
• C++ exception-handling techniques enable programmers to design
exception-handling code that executes an alternate path if a run-time error
occurs.
• This approach does not require interpreting error and exception-handling
code with the rest of the code.
Exception Handling
• C++ exception-handling tools should be used in the following situations:
• To separate the error-handling code from the main code, in order to
improve program readability and maintainability
• To process exceptions in a uniform way
• To process exceptions before they cause a system failure
• To separate the exception processing from the error detection section of
a program (this is particularly appropriate when developing large projects
by a team of programmers)
• To properly terminate a program in situations when it cannot recover
• To process errors caused by library functions
• Exception handling should only be used for processing runtime errors.
Using try-throw-catch
• C++ provides a structure that uses the try, throw, and catch keywords to
process run-time errors.
try
{
if (divide(5, input) == 1)
{
throw "Error: Input cannot be 5 ";
}
}
catch (const char* err)
{
cout << msg << endl;
}
• A try block must be followed by at least one catch statement, or a syntax error
will be generated
Using try-throw-catch
• The try keyword is used to mark a code segment (try block), which will be
monitored for errors.
• A try block can contain any part (small or large) of a program, as well as the
code of the entire main() function.
• A program can have as many try blocks as necessary, including nested try
blocks.
• If an error (exception) is detected within a try block during program
execution, the exception is thrown by the throw keyword to a catch block
following the try block.
• Throwing an exception results in transferring control from the try block to the
appropriate exception handler code within a catch block that is designed to
process that particular type of the exception.
Throwing and Handling an Exception
• Throwing an exception transfers control to an exception handler within a
catch block.
• This process commonly involves passing an object from the point where the
exception occurred to the exception-handler code, which can use that object
to process the exception.
• A selection of a catch block contains appropriate exception-handler code
based on the type of the object passed (thrown).
• The type of the object thrown by throw must match the type of the argument
in one of the catch statements following the try block, or the exception will
not be handled.
• Throwing an unhandled exception (there is no appropriate catch statement
for this type of exception) automatically invokes the terminate() function,
which terminates the program by calling the abort() function.
Throwing and Handling an Exception
• Note that the throw keyword can be followed by a variable (object),
constant, expression, or a function call, including a class constructor.
• The expression that follows throw is evaluated first and then its value
(object) is thrown to a corresponding catch statement containing an
argument - the type of which matches the type of the value (object) thrown.
• A throw expression can be omitted; that is, the throw keyword can be
followed directly by a semicolon indicating the end of the throw statement.
• In this case, the current exception will be passed (rethrown) to an outer try
/catch block.
• After an exception is thrown from a function, the function terminates and all
locally declared, non-static variables are destroyed (same as when a
function returns).
Throwing and Handling an Exception
• When the Function is called again, the exception-handling mechanism is reset
• Use exception handling to process exceptions generated in library functions in
order to prevent certain types of run-time errors when using these functions
• Throwing an exception transfers control from a try block to an exception handler.
• This action results in the destruction of each variable declared in the try
block.
• In addition to a throw statement, program control can be transferred out of a try
block(or exception handler) by using break, return, continue, or go to
statements.
• This will also result in the destruction of all variables declared within the try
block or exception handler.
Throwing and Handling an Exception
• The following code fragment demonstrates a nested try block, and uses a goto statement to
transfer control from the inner try block to the beginning of the outer try block:
lab1: try {
int x;
try
{
double y;
if (condition)
goto lab1;
} // inner try block ends
catch (...)
{
}
}// outer try block ends
catch (...)
{
}
Throwing and Handling an Exception
• C++ provides a special kind of a catch statement that can be used to catch
an exception of any type.
• General Format:
catch(…)
{
// Exception Handler
}
Throwing and Handling an Exception
• When an exception is thrown, C++ searches for the first catch statement that
could handle the exception
• Either a catch statement with an argument type that matches the type of the
object thrown, or a catch statement with ellipses.
• A catch statement with ellipses has to be the last catch statement that follows
a try block (after all other catch statements with arguments of specific types), or
a syntax error will be generated.
• This is logical as C++ searches for the first match when handling exceptions and
catch(...) can handle all exceptions.
• The catch(...) statement (also known as a catch all statement) should be
used as a default exception handler (similar to default in a switch statement) to
process exceptions that cannot be handled by other catch statements.
• Notice that throwing an unhandled exception causes abnormal program
termination.
Exception Specifications
• A function that is called from within a try block, or any other function called
from that function, can throw exceptions.
• C++ uses an exception-specification list to restrict types of exceptions that
can be thrown directly or indirectly from a function.
• An exception specification has the general format shown below:
• throw(list,of,types)
• An exception specification can appear only on a function
declaration/definition or a function pointer declaration, as shown in the
following example:
• void func1(int) throw(double, char*); // function prototype
• void(*ptr)() throw (int); // function pointer declaration
Exception Specifications
• An attempt to throw an exception from a function, where the exception type
is not listed in the function exception-specification list, will cause abnormal
program termination.
• C++ automatically invokes the unexpected() function in this case; which, by
default, calls the terminate() function to terminate the program.
• Programmers can also design their own code to handle unexpected
exceptions.
• That code would execute (instead of the default unexpected() function)
when an unexpected exception occurs.
• An exception-specification list can be empty, specifying that a function
cannot throw any exception. This results in the unexpected() function
call.
• float compute(int, float) throw ();
Exception Specifications
• C++ does not check types of exceptions at compile time. Should a function
contain an exception that is not listed in its exception-specification list, no
syntax error is generated.
• Instead, an unexpected exception will be generated at run-time by invoking
the unexpected() function
• By default, the unexpected() function terminates the program by calling the
terminate() function.
• In some cases program termination is not desired.
• To prevent an automatic call to the default unexpected() function,
programmers might want to design their own function that will be invoked
each time an unexpected exception occurs.
Exception Specifications
• To set up a user-defined function as a handler of unexpected exceptions in a
program, the set_unexpected() function (defined in the exception header file) is
used.
• The following code segment demonstrates this technique:
• void handleUnexpected(){};
• set_unexpected(handleUnexpected);
• The technique demonstrated in the previous example can be used to create and
set up a user-defined function that will be invoked instead of the default
terminate() function to take care of unhandled (uncaught) exceptions.
• The set_terminate() function (defined in exception) is used to set up the user-
defined function that replaces terminate() as an uncaught exception handler.
• NOTE: A function without an exception-specification list can throw any exception.
Handling Memory Allocation Errors
• C++ dynamic memory allocation (run-time memory allocation), which uses
new and delete operators, is a very powerful and frequently used tool.
• The new operator may fail to successfully allocate memory (insufficient
resources), causing a run-time error.
• C++ provides a variety of tools and techniques to handle memory allocation
failures- some of which are discussed in this section.
• A simple method to handle a memory allocation error is to check for a null
pointer returned by new when memory allocation fails.
• The following code fragment demonstrates this method:
• int* ptr = new int[100];
• if(ptr ==0) {/* Handle Error Here */}
Handling Memory Allocation Errors
• Similar results can be achieved with less coding by using the assert() macro that
is defined in the assert header file.
• The following code fragment demonstrates the use of assert() when processing
new failures:
• double *dptr
• new double[100] ;
• assert (dptr!=0); //Handles a memory allocation error
• The assert() macro tests the value of the expression (condition) that is passed to
the macro as an argument.
• If the value is false, assert() displays an error message and invokes the abort()
function (similar to exit()) to terminate the program.
• This macro can also be used as a powerful debugging tool when testing
variables and expressions.
Handling Memory Allocation Errors
• C++ enables programmers to design their own recovery routine (a user-
defined function) to handle memory allocation errors.
• To register a user-defined function as a default handler of new failures, the
set_new_handler() function (defined in the new header file) is called.
• For example, the following statement placed at the beginning of the main()
function will register the my_handler() user-defined function as a default
handler of new failures:
• set new handler (my_handler) ;
• A call to set_new_handler() with the 0 argument removes the current new
handler, resulting in a program without a default new handler.
• The set_new_handler() function takes a function pointer as an argument.
• A function pointed to by the pointer will automatically be invoked each time
new fails.
Exceptions and Classes
• A class object can be thrown when an exception occurs.
• The C++ library (exception header file) contains classes that are used to
handle exceptions.
• A standard library class named exception is commonly used.
• This class also serves as the base class for all classes that handle
exceptions thrown by certain expressions.
• Note that all member functions of the exception class contain an exception-
specification list with no types specified (throw ()), and as a result, these
functions cannot throw any exception.
• The exception class is used as the base class for other classes that handle
specific types of exceptions.
• The what() virtual function, which returns a constant string (an exception
description), can be overridden by any class derived from exception.
Exceptions and Classes
• The skeleton of the standard exception class is shown below:
class exception
{
public:
exception() throw(); // constructor
exception(const exception& rhs) throw(); // copy constructor
exception& operator=(const exception& rhs) throw();
virtual ~exception() throw(); // destructor
virtual const char* what() const throw();
};
• Example:
• throw CustomException();
• catch(exception exc){ cout <<exc.what(); }
Exceptions and Classes
• Destructors are called to destroy all objects instantiated within a try block before
any exception is thrown from the block.
• The exc object of the exception class on the last slide, which serves as an
argument of the catch statement can be used to invoke the what() library
member function when handling an exception, as follows:
• By default, a string constant Unexpected exception is returned by what().
• The what() function, can be redefined (overridden) by classes derived from
exception and return different string constants.
• In addition to standard classes defined in the C++ library, user-defined classes
can also be used to process exceptions.
• Exception classes can have data members and member functions as normal
classes do.
• They can use constructors, destructors, virtual functions, and can be organized
into an inheritance hierarchy of classes.
Exception Handling | Summary
• A C++ exception is a problem encountered at run-time

• C++ exceptions allow the program to handle catastrophic errors during the
execution of the program, and avoid crashing or behaving unexpectedly

• C++ exceptions are built upon three keywords: try, throw, and catch

• The try keyword allows us to write a block of code to be tested for any run-time
errors

• The throw keyword throws an exception when a run-time problem is detected

• The catch keyword defines a block of code to be executed when the thrown
exception has occurred inside the try block

26
Exception Handling – Example
#include <iostream>
• Division by zero is a common using namespace std;
double division(int a, int b) {
run-time error that could only be if (b == 0) {
throw "DIVISION BY ZERO";
caught during execution (not }
return (a / b);
compilation) }
int main() {
int x = 50;
int y = 0;
double z = 0;
• Since the exception we are
try {
raising is of type const char*, z = division(x, y);
cout << z << endl;
we must ensure that the catch }
catch (const char* msg) {
statement also uses the type cerr << msg << endl;
}
const char* return 0;
}

Page 27
Exception Handling – Example
#include <iostream>
• One could also throw using namespace std;
specific numbers for error int main()
{
handling and identification try {
int age = 15;
if (age > 18) {
• The catch keyword could cout << "Access granted.";
}
accept ellipsis to catch else {
any type of exception for throw 8724;
robust program execution }
}
catch (int myNum) {
cout << "Access denied - You must be at least 18 years old."
catch (…){ << endl;
cout << "Error number: " << myNum << endl;
}
} }

Page 28
Throwing Exception Classes
• This program specifies multiple #include <iostream> #include <iostream>
exception classes (i.e. Full() using namespace std; using namespace std;
const int MAX = 3; //stack holds 3 integers int main()
and Empty()) within the Stack class Stack {
class { Stack s1;
• When an exception occurs in this private: try
class, nameless temporary int st[MAX]; //stack: array of integers {
int top; //index of top of stack s1.push(11);
objects of the exception classes public: s1.push(22);
are thrown class Full { }; //exception class s1.push(33);
• During execution, the try-catch class Empty { }; //exception class s1.push(44); //oops: stack full
block can catch the exception if Stack() //constructor cout << "1: " << s1.pop() << endl;
{
it is thrown from inside of the cout << "2: " << s1.pop() << endl;
top = -1; cout << "3: " << s1.pop() << endl;
Stack class methods } //oops: stack empty
• All the catch blocks used with a void push(int var) //put number on stack cout << "4: " << s1.pop() << endl;
try block must immediately { }
follow the try block if (top >= MAX - 1) //if stack full, catch (Stack::Full)
throw Full(); //throw Full exception {
• A group of catch blocks, or a st[++top] = var; cout << "Exception: Stack Full" << endl;
catch ladder, operates similarly to } }
a switch-case block int pop() //take number off stack catch (Stack::Empty)
{ {
if (top < 0) //if stack empty, cout << "Exception: Stack Empty" << endl;
throw Empty(); //throw Empty exception }
return st[top--]; return 0;
} }
};

29
More Involved Example
#include <iostream> int main()
using namespace std;
class Distance //Imperial Distance class {
{ try
private:
{
int feet;
float inches; Distance dist1(17, 13.5); //2-arg constructor
public: Distance dist2; //no-arg constructor
class InchesEx //exception class
{ dist2.getdist(); //get value
public: //display distances
const char* origin; //for name of routine
cout << "\ndist1 = "; dist1.showdist();
float iValue; //for faulty inches value
InchesEx(const char* o, float i) : origin(o), iValue(i) //2-arg constructor cout << "\ndist2 = "; dist2.showdist();
{ } }
}; //end of exception class
Distance() //constructor (no args) catch (Distance::InchesEx ix) //exception handler
{ {
feet = 0; inches = 0.0;
cout << "\nInitialization error in " << ix.origin
}
Distance(int ft, float in) //constructor (two args) << ".\n Inches value of " << ix.iValue
{ << " is too large.";
if (in >= 12.0)
throw InchesEx("2 - arg constructor", in); }
feet = ft; cout << endl;
inches = in;
return 0;
}
void getdist() //get length from user }
{
cout << "\nEnter feet : "; cin >> feet;
cout << "Enter inches : "; cin >> inches;
if (inches >= 12.0)
throw InchesEx("getdist() function", inches);
}
void showdist() //display distance
{
cout << feet << "\' - " << inches << "\'";
}
};

30
Additional Resources
• https://www.tutorialspoint.com/cplusplus/cpp_exceptions_handling.htm
• https://www.w3schools.com/cpp/cpp_exceptions.asp
• https://www.geeksforgeeks.org/exception-handling-c/
• https://www.cplusplus.com/doc/tutorial/exceptions/
TEST REVIEW (Lab Test #2)
• Lab Period Wednesday April 6th 2022
• Worth 16%
• All topics from Week 1 – Week 11 will be covered.
• Inheritance
• File Input
• File Output
• Polymorphism
• Virtual Functions
• Composition

• NOTE: Make sure you have a computer with Visual Studio prior to the lab
test. There will be no extensions, make up tests or bonus assignments.

You might also like