CPSC 427: Object-Oriented Programming
Michael J. Fischer
Rethrowing Exceptions
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
20a-Exceptions-rethrow)
1 #include <iostream>
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 }
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:
Uncaught Exceptions
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.
Singleton Design Pattern
Unique
IDs
Unique identifiers (UIDs) are familiar in many situations: Appliance
serial numbers, automobile VINs, social security numbers, and so
forth.
They are useful in programming as well. Whenever a class has many
instances, UIDs can help track objects from the time they are created
until their eventual deletion. This is especially helpful when custody
changes during the lifetime of the object.
UIDs are very helpful in identifying error comments during debugging. They are also helpful when included in log files.
How to generate UIDs
A method for adding UIDs to class instances involves the following steps:
Recall that static variables cannot be initialized within the class but rather in a .cpp file.
Drawbacks to the simple method
Drawbacks to the simple approach:
A UID generator
What we want is a class Serial with a private data member nextUID
and a public function uidGen() that returns and updates the next
UID.
In order to call the function, we need a class instance uidGen of
Serial that initializes nextUID and supports the public function
uidGen(). Now, to generate a new serial number, simply call
uidGen.uidGen().
However, this solution has two problems:
Singleton class
A singleton class solves both problems.
Functors
A functor is an object that acts like a function.
Let obj be a functor. Then one can write obj(), pretending that obj is
a function.
All that is needed to make this work is to define operator() within the
class.
For our UID generator, we define the behavior of obj to be the same as for uidGen() discussed above.
Serial.hpp
Serial.cpp
Smart Pointer Demo
Dangling
pointers
Pointers can be used to permit object sharing from different contexts.
One can have a single object of some type T with many pointers in different contexts that all point to that object.
Problems with shared objects
If the different contexts have different lifetimes, the problem is to know
when it is safe to delete the object.
It can be difficult to know when an object should be deleted. Failure to
delete an object will cause memory leaks.
If the object is deleted while there are still points pointing to
it, then those pointers become invalid. We call these dangling
pointers.
Failure to delete or premature deletion of objects are common sources of errors in C++.
Avoiding dangling pointers
There are several ways to avoid dangling pointers.
Modern C++ Smart Pointers
Modern C++ has three kinds of smart pointers. These are objects that act very much like raw pointers, but they take responsibility for managing the objects they point at and deleting them when appropriate.
We will discuss them later in the course, time permitting. For now, we present a simplified version of shared pointer so that you can see the basic mechanism that underlies all of the various kinds of shared pointers.
Smart pointers
We define a class SPtr of reference-counted pointer-like objects.
An SPtr should act like a pointer to a T.
This means if sp is an SPtr, then *sp is a T&.
We need a way to create a smart pointer and to create copies of
them.
Demo 20b-SmartPointer illustrates how this can be done.