234122 מת”מ
C++ : Exceptions
1
2
Motivation
main
call
Chapter 9
processFile
Error handled:
Free some
resources
getInput
Exceptions
vectorInsert
vectorResize
Error Handling in C++
Error occurs:
out of
memory
new
3
4
Error Handling in C
Error Handling in C
How may a function react when encountering an
unexpected/erroneous situation ?
• It is very easy to neglect checking for
if ((res = scanf("%d",&n))<1) {
fprintf(cerr,"Input error");
return -1;
}
if ((p = malloc(n*sizeof(int))) == NULL) {
fprintf(cerr,"Memory error");
return -1;
}
for (i=0; i<n; ++i) {
if ((res = scanf("%d",&x))<1) {
fprintf(cerr,"Input error");
free p;
return -1;
}
p[i] = x;
}
errors
• Return an impossible value:
Set
int
SetCreate(…);
atoi(const char*);
// OK: return NULL on error
// What to return for “bad-str” ?
• Code that handles all possible errors
may quickly inflate
• Return a special type:
Result sqrt(double arg, double* res); // Composition now impossible
• The user reports an interest:
double sqrt(double, Result* r);
// r may be NULL
• There is no systematic way of passing
control from the place where the error
occurred to the function that can handle
it, resulting in explicit error propagation
• Provide a global mechanism:
E.g,
errno
in C
5
The Challenge in C++
• There are circumstances where none of the previous
error reporting styles can work:
– in constructors (no return value so Result argument is futile)
– in overloaded operators
Rational r = (a + b)*(c + d/e);
6
Example
// compute arithmetic operations
// between two rationals
Rational compute() {
Rational r1, r2;
char op;
cin >> r1 >> op >> r2;
switch op {
case '+': return r1+r2;
case '-': return r1-r2;
case '*': return r1*r2;
case '/': return r1/r2;
default: // error
}
}
compute
operator>>
Rational
normalize
Copyright 2011
C. Gotsman & R. Rubinstein
Computer Science Dept. Technion
res = f(x,y);
if (res<0) {
return res;
}
iostream& operator>>(istream& in, Rational& r) {
int top, bottom;
char slash;
in >> top >> slash >> bottom;
if (!in) {
// invalid format
}
if (slash != '/') {
// unexpected character
}
r = Rational(top,bottom);
}
Rational::Rational(int top=0, int bottom=1) :
t(top), b(bottom) {
normalize();
}
Rational::normalize() {
if (b==0) {
// error - but what to do?
}
if (b<0) {
b=-b;
t=-t;
}
int divisor = gcd(abs(t),b);
t/=divisor;
b/=divisor;
}
For personal use
only
Page 1
234122 מת”מ
C++ : Exceptions
7
The Throw-Catch Game
Error handling is a path parallel to the return path
call
catch
return
Rational compute() {
Rational r1, r2;
char op;
// does not handle any exception
cin >> r1 >> op >> r2;
switch op {
case '+': return r1+r2;
case '-': return r1-r2;
case '*': return r1*r2;
case '/': return r1/r2;
default: // error
}
}
throw
Example – Take II
class Rational {
public:
class ZeroDenom {};
int main() {
differentiation
class InvalidFormat {};
while (1) {
…
cout << "Enter command: ";
}
istream& operator>>(istream& in, Rational& r) {
try {
Rational result = compute();
int top, bottom;
char slash;
}
catch(const Rational::InvalidFormat&) {
in >> top >> slash >> bottom;
if (!in) {
cout << "Error: invalid format,
please try again" << endl;
throw Rational::InvalidFormat();
}
fflush(stdin);
continue;
if (slash != '/') {
}
throw Rational::InvalidFormat();
catch(const Rational::ZeroDenom&) {
}
cout << "Error: zero denominator,
r = Rational(top,bottom);
}
please try again" << endl;
fflush(stdin);
Rational::Rational(int top=0, int bottom=1) :
continue;
t(top), b(bottom) {
}
// does not handle any exception
cout << "Result = " << result << endl;
normalize();
}
}
return 0;
}
Rational::normalize() {
Rational compute() {
…
// does not handle any exception
cin >> r1 >> op >> r2;
…
}
8
Example – Take I
istream& operator>>(istream& in, Rational& r) {
…
// does not handle any exception
r = Rational(top,bottom);
}
Rational::Rational(int top=0, int bottom=1) :
t(top), b(bottom) {
// does not handle any exception
normalize();
}
Rational::normalize() {
if (b==0) {
throw string("zero denominator");
}
…
}
catch
main
compute
operator>>
Rational
throw
normalize
9
10
The Throw-Catch Game
1. When an error occurs, a function may throw an exception using the command
throw E;, where E is the object to throw.
2. Throwing an exception causes the function to terminate immediately, and pass E to
the calling function.
3. The calling function can catch the exception using a try-catch block. Putting code
within a try { … } block means that the function wishes to monitor for exceptions
that may be thrown by this code, and handle some or all of them.
4. The catch command specifies the type of exception it can handle. If anywhere within
the try block an exception of that type is thrown, the code in the try block stops
and control is passed to the catch block. After the catch, execution resumes after
the try-catch block.
5. If an exception is thrown and the calling function does not handle this type of
exception, the calling function will also terminate and pass the exception to its calling
function.
if (b==0) {
throw ZeroDenom();
}
…
6. An uncaught exception keeps propagating up the call stack and terminating functions
until it is caught. If it reaches main and is never caught, the program terminates
altogether.
}
Example – Take III
class Rational {
public:
class RationalException: public std::exception {};
class ZeroDenom: public RationalException {};
class InvalidFormat: public RationalException {};
…
}
int main() {
while (1) {
cout << "Enter command: ";
try {
Rational result = compute();
}
catch(const Rational::InvalidFormat&)
catch(const Rational::ZeroDenom&)
catch(const Rational::RationalException&)
catch(const std::exception&)
}
return 0;
}
Rational compute() {
…
// does not handle any exception
cin >> r1 >> op >> r2;
…
}
int main() {
while (1) {
cout << "Enter command: ";
try {
Rational result = compute();
}
catch(const string& s) {
cout << "Error: " << s << endl;
cout << "Please try again" << endl;
fflush(stdin);
continue;
}
cout << "Result = " << result << endl;
}
catch is by type
return 0;
}
{
{
{
{
catch is sequential,
so always list base
class last
…
…
…
…
}
}
}
}
11
Generality: A base class catch
will also be activated by
any derived class
istream& operator>>(istream& in, Rational& r) {
…
in >> top >> slash >> bottom;
if (!in) {
throw Rational::InvalidFormat();
}
if (slash != '/') {
throw Rational::InvalidFormat();
}
r = Rational(top,bottom);
}
Rational::Rational(int top=0, int bottom=1
t(top), b(bottom) {
// does not handle any exception
normalize();
}
Rational::normalize() {
if (b==0) {
throw ZeroDenom();
}
…
}
Copyright 2011
C. Gotsman & R. Rubinstein
Computer Science Dept. Technion
Example – Take IV
using namespace
12
namespace Exceptions {
class IOException: public std::exception {};
class LogicalException: public std::exception {};
…
}
class Rational {
using Exceptions;
int main() {
public:
while (1) {
class ZeroDenom: public LogicalException {};
cout << "Enter command: ";
class InvalidFormat: public IOException {};
try {
…
Rational result = compute();
};
}
catch(const Rational::InvalidFormat&)
{ … }
// I/O error reading rational
catch(const Rational::ZeroDenom&)
{ … }
// zero denominator
catch(const Exceptions::LogicalException&) { … }
// arithmetic operator was not
//
one of the four supported
catch(const std::exception&)
{ … }
// general exception
}
return 0;
}
Rational compute() {
...
switch op {
case '+': return r1+r2;
case '-': return r1-r2;
case '*': return r1*r2;
case '/': return r1/r2;
default: throw Exceptions::LogicalException();
}
For personal use
only
Page 2
234122 מת”מ
C++ : Exceptions
13
int main() {
while (1) {
cout << "Enter command: ";
try {
using what()
Rational result = compute();
}
catch(const Rational::InvalidFormat&)
{ … }
// I/O error reading rational
catch(const Rational::ZeroDenom&)
{ … }
// zero denominator read
catch(const Exceptions::LogicalException& e) {
// arithmetic operator was not one of the four supported
cerr << e.what() << endl;
…
}
namespace Exceptions {
catch (const std::exception& e) {
class BasicException: public std::exception {
private:
// we did not anticipate this !
const char* errmsg;
cerr << e.what() << endl;
public:
return 1;
BasicException(const char* msg = "")
}
: errmsg(msg) {}
}
const char* what() const { return errmsg; }
return 0;
};
class LogicalException: public BasicException {
}
public:
Rational compute() {
LogicalException(const char* msg = "")
…
: BasicException(msg) {}
switch op {
};
case '+': return r1+r2;
…
case '-': return r1-r2;
}
case '*': return r1*r2;
case '/': return r1/r2;
default: throw Exceptions::LogicalException("Invalid arithmetic operator");
}
class exception {
public:
exception();
exception(const exception&);
exception& operator=(const exception&);
virtual ~exception();
virtual const char* what() const;
// there are versions with extra methods
};
15
Error Handling by Constructors
• A constructor may catch an exception thrown during initialization
String::String(const char* str)
try
: length(strlen(str)), s(strcpy(new char[strlen(str)+1],str))
{
thrown if new
// constructor code
fails for lack of
}
memory
catch(std::bad_alloc& e) {
cerr << e.what();
throw;
// pass exception upwards
}
• The original exception is thrown by throw, even if base is caught
Copyright 2011
C. Gotsman & R. Rubinstein
Computer Science Dept. Technion
14
Example – Take V
The Exception Class
16
Exceptions – Final Points
1. Stack unwinding: as an exception propagates up the stack and terminates functions,
it calls the destructors of all the automatic objects, as a return command would
do. Dynamically allocated objects must be freed manually.
2. Passing an exception to a catch command is done just like function argument
passing. Thus, catch(string s) passes the thrown string by value (copy
constructor) whereas catch(const string& s) passes by reference. Passing by
reference is always preferred since a copy constructor can itself throw an exception.
3. If during stack unwinding a second exception is thrown (before we reach a catch
block for the current exception), the program terminates. Thus, destructors should
never throw exceptions.
4. A single try block can have multiple catch blocks for different types of exceptions.
If an exception is thrown, the catch blocks are scanned sequentially until the first
match is found.
5. An object of a derived class can be caught by a handler for its base class. Thus, a
single catch can handle multiple exception types having a base type in common.
Thus, base classes should be caught last.
For personal use
only
Page 3
© Copyright 2026 Paperzz