CPSC 427: Object-Oriented Programming
Michael J. Fischer
Exceptions (continued)
Standard exception class The standard C++ library provides a polymorphic base class std::exception from which all exceptions thrown by components of the C++ Standard library are derived.
These are:
exception | description |
bad_alloc | thrown by new on allocation failure |
bad_cast | thrown by a failed dynamic_cast |
bad_exception | thrown when an exception type doesn’t match any catch |
bad_typeid | thrown by typeid |
ios_base::failure | thrown by functions in the iostream library |
(from http://www.cplusplus.com/doc/tutorial/exceptions/)
Catching standard exceptions
Class std::exception contains a virtual function Because the base class is polymorphic, it is possible to write a single
catch handler that will catch all derived exception objects. Example:
Deriving
your
own
exception
classes
from
std::exception
Multiple
catch
blocks
Demo 21d-Exceptions-cards has an example of this as well.
Rethrow
A catch block can do some processing and then optionally rethrow the
exception or throw a new exception.
A
subtle
fact
about
rethrow
Rethrowing the current exception is not the same as throwing an
exception with the same exception object. throw e; always copies object e to special memory using the copy
constructor for e’s class. throw; does not make another copy of the exception object but instead
uses the copy already in special memory. This difference becomes apparent if the copy is not identical to the
original (possible for a custom copy constructor), or if the copy
constructor has side effects (such as printing output).
Example
of
rethrowing
an
exception
(demo
22a-Exceptions-rethrow)
1 #include <iostream>
Results
In the preceding example, the throw myex on line 12 causes a copy, but
the throw on line 14 does not. This produces the following output:
Throw
restrictions
It is possible to specify that a function can only throw certain kinds of
exceptions (or none at all). This “feature” is regarded as a bad idea because the current semantics
are not what one would expect. It does not prevent the exceptions from being thrown; rather, it causes a
run-time test to be inserted which calls unexpected_exception()
when an exception is thrown that is not listed in the function’s throw
specifier.
Uncaught
exceptions:
Ariane
5
Uncaught exceptions have led to spectacular disasters. The European Space Agency’s Ariane 5 Flight 501 was destroyed 40
seconds after takeoff (June 4, 1996). The US$1 billion prototype rocket
self-destructed due to a bug in the on-board guidance software.
[Wikipedia] This is not about a programming error.
It is about system-engineering and design failures.
The software did what it was designed to do and what it was agreed that
it should do.
Uncaught
exceptions:
Ariane
5
(cont.)
Heres a summary of the events and its import for system engineering:
As the result of the unanticipated failure mode and a diagnostic message
erroneously treated as data, the guidance system ordered violent
attitude correction. The ensuing disintegration of the over-stressed
vehicle triggered the pyrotechnic destruction of the launcher and its
payload.
Termination
There are various conditions under which the exception-handling
mechanism can fail. Two such examples are:
When this happens, the function terminate() is called, which by default aborts
the process.1
This is a bad thing in production code.
Conclusion: All exceptions should be caught and dealt with explicitly.
Code Reuse Reusable
code
One of the major goals of C++ is code reusability. The desire to reuse code occurs in two very different scenarios.
Sharing
Different
parts
of
a
single
application
need
the
same
or
similar
code
blocks.
The
code
should
be
written
once
and
shared
by
the
parts
that
need
it.
Mechanisms
for
code
sharing
include
functions
and
(non-polymorphic)
derivation.
Libraries
A
code
base
is
made
available
for
others
to
use
in
their
applications,
e.g.,
the
C++
Standard
Library.
Useful
mechanisms
include
polymorphic
derivation
and
templates.
Problems
with
reusing
code
The problem with code reuse is that one rarely wants to reuse the exact
same piece of code. Rather, one wants similar code but specialized to a
particular application. For example, most useful functions take parameters which tell the
function what data to compute on. Different applications can call the
function with their own data. Similarly, containers such as vector make sense for many different kinds
of objects. The STL container vector<T> allows the generic vector to
be specialized to any suitable type T object.
How
to
allow
variability
Code reusability becomes the problem of bridging the gap between
abstract general code and specific concrete application.
In all three cases, application-specific data must satisfy the constraints
required by the general code.
Specifying
constraints
One of the important mechanisms in C++ for specifying constraints is
the type system.
Linear Containers Demo
19-Virtual
Linked list code was introduced in demo 08-BarGraph, where a Row was
a linked list of Item*, where an Item contained exam information for a
particular student. Demo 19-Virtual extracted the linked list code from Row and called it
Linear. The specific Item class from 08-BarGraph was renamed Exam,
and the type Item was reserved for the kind of object that could be put
in a linked list.
Sharing
in
19-Virtual
19-Virtual observes that stacks and queues are very similar data
structures.
The common code is in the base class Linear. The derived classes
Stack and Queue override virtual base class functions as needed for their
specializations.
Abstract
containers
Both Stack and Queue are examples of list containers that support four
operations: put, pop, peek, and print. Class Container is an abstract base class with a virtual destructor and
four pure abstract functions put, pop, peek, and print. Linear is derived from Container. This ensures that any generic code
for dealing with containers can handle Linear objects. However, Container is general on only one dimension. It is still specific
to containers of Item* objects.
Ordered Containers Demo
22b-Multiple
The purpose of demo 22b-Multiple is to generalize the linear
containers of demo 19-Virtual to support lists of items that are sorted
according to a data-specific ordering. It does this by adding class Ordered and Item, creating two ordered
containers of type class List and class PQueue, and extending the
code appropriately.
Ordered
base
class
Ordered is an abstract class (interface) that promises items can be
ordered based on an associated key. It promises functions:
Use:
class Item : public Exam, Ordered { ... }; Note: We can use private derivation because every function in Ordered
is abstract and therefore must be overridden in Item.
class
Item
Item is publicly derived from Exam, so it has access to Exam’s public and
protected members. It fulfills the promises of Ordered by defining:
KeyType is defined with a typedef in exam.hpp to be int.
Container
base
class
We saw the Container abstract class in demo 19-Virtual. It promises
four functions:
Use: class Linear : Container { ... };
Additions
to
Linear
The meaning of put(), pop(), and peek() for ordered lists is
different from the unordered version, even though the interface is the
same. The concept of a cursor is introduced into Linear along with new
virtual functions insert() and focus() for manipulating the
cursor. peek() and pop() always refer to the position of the cursor. put()
inserts into the middle of the list in order to keep the list properly
sorted.
class
Linear
Linear implements general lists through the use of a cursor, a pair of
private Cell pointers here and prior. Protected insert() inserts at the cursor.
Protected focus() is virtual and must be overridden in each derived
class to set the cursor appropriately for insertion. Cursors are accessed and manipulated through protected functions
reset(), end(), and operator ++(). Use:
List::insert(Cell* cp) {reset(); Linear::insert(cp);}
inserts at the beginning of the list.
class
PQueue
PQueue inserts into a sorted list.
Note the use of the comparison between a PQueue and a Cell*. This is defined in linear.hpp using the cursor:
bool operator< (Cell* cp) {
return (*cp->data < *here->data); }
Multiple Inheritance What
is
multiple
inheritance
Multiple inheritance simply means deriving a class from two or more
base classes. Recall from demo 22b-Multiple:
class Item : public Exam, Ordered { ... }; Here, Item is derived from both Exam and from Ordered.
Object
structure
Suppose class A is multiply derived from both B and C.
We write this as class A : B, C { ... };. Each instance of A has “embedded” within it an instance of B and an
instance of C. All data members of both B and C are present in the instance, even if
they are not visible from within A. Derivation from each base class can be separately controlled with privacy
keywords, e.g.:
class A : public B, protected C { ... };.
Diamond
pattern
One interesting case is the diamond pattern.
Each instance of A contains two instances of D—one in B and one in
C. These can be distinguished using qualified names.
Suppose x is a public data member of D.
le Within A, can write B::D::x to refer to the first copy, and C::D::x
to refer to the second copy.
Virtual
derivation
Virtual derivation allows instances of A to have only a single embedded
instance of D.
D’s constructor is the first thing called when A is created, and it’s up to A
to supply any needed parameters in its ctor.
Template Example Using
templates
with
polymorphic
derivation
To illustrate templates, I converted 22b-Multiple to use template
classes. The result is in 22c-Multiple-template. There is much to be learned from this example.
Today I point out only a few features.
Container
class
hierarchy
As before, we have PQueue derived from Linear derived from
Container. Now, each of these have become template classes with parameter class
T.
T is the item type; the queue stores elements of type T*. The main program creates a priority queue using
Item
class
hierarchy
As before, we have Item derived from Exam, Ordered. Item is an adaptor class.
It bridges the requirements of PQueue<T> to the Exam class.
Ordered
template
class
Ordered<KeyType> describes an abstract interface for a total ordering
on elements of abstract type KeyType. Item derives from Ordered<KeyType>, where KeyType is defined in
exam.hpp using a typedef. An Ordered<KeyType> requires the following:
That is, there is the notion of a sort key. key() returns the key from an
object satisfying the interface, and two keys can be compared using <
and ==.
Alternative
Ordered
interfaces
As a still more abstract alternative, one could require only comparison
operators on abstract elements (of type Ordered). That is, the interface
would have only two promises:]
This has the advantage of not requiring an explicit key, but it’s also less
general since keys are often used to locate elements (as is done in the
demo).
{
cerr << "exception caught: " << e.what() << endl;
}
#include <exception>
using namespace std;
class myexception: public exception {
virtual const char* what() const throw()
{ return "My exception happened"; }
} myex; // declares class and instantiates it
int main () {
try {
throw myex;
}
catch (exception& e) {
cout << e.what() << endl;
}
return 0;
}
2 using namespace std;
3 class MyException {
4 public:
5 MyException() {}
6 MyException( MyException& e ) {
7 cout << "Copy constructor called\n"; }
8 ~MyException() {}
9 } myex; // declares class and instantiates it
10 int main () {
11 try {
12 try { throw myex; }
13 catch (MyException& e) {
14 cout << "Exception caught by inner catch\n"; throw; }
15 }
16 catch (MyException& err) {
17 cout << "Exception caught by outer catch\n";
18 }
19 return 0;
20 }
Exception caught by inner catch
Exception caught by outer catch
operator==(const KeyType& k) const { return key() == k; }
bool
operator< (const KeyType& k) const { return key() < k; }
bool
operator< (const Item& s) const { return key() < s.key(); }
virtual Item* pop() =0; // Remove Item
virtual Item* peek() =0; // Look at Item
virtual ostream& print(ostream&) =0; // Print all Items
for (reset(); !end(); ++*this) { // find insertion spot.
if ( !(*this < cp) )break;
}
Linear::insert( cp ); // do the insertion.
}
class B : public D { ... };
class C : public D { ... };
class A : public B, public C { ... };
class B : public virtual D { ... };
class C : public virtual D { ... };
class A : public B, public C { ... };
virtual bool operator < (const KeyType&) const =0;
virtual bool operator == (const KeyType&) const =0;
virtual bool operator == (const Ordered&) const =0;