Run-Time Behaviour in C ++
9.10 EXERCISES AND RESEARCH
1. What are the most common exceptions that you encounter in your daily work? What are they caused by? How would you like to resolve them?
And finally, how would you implement the corresponding exception classes in C++ for these problems?
2. In Chapter 7 we created a hierarchy of C++ classes for solving nonlinear real-valued equations in a single variable. But there is no provision for exception handling. Typical problems include:
r
At some stage during the execution of the iterative algorithm we may try to divide byr
zeroThe algorithm may not converge to the exact solution; in fact it just goes ‘on and on’; you will need to build a counter that represents the total number of allowed iterations before the algorithm ‘gives up’Answer the following questions:
(a) Determine how you are going to redesign the class hierarchy to accommodate these new requirements
(b) Implement the requirements in C++ and use the class DatasimException (c) Test the program again
3. We consider the class hierarchy in section 9.3 again and the code that creates an array of base class pointers to Base. This array is populated by a ‘mix’ of instances of classes D1 and D2.
The objective of this exercise is to create a ‘Bill of Materials’ that tells us how many instances of each class there are.
4. Make an inventory of the most common run-time errors and exceptions that can occur in your applications. For example, errors occur for various reasons:
r
Bad input/outputr
Range and out-of-bounds errorsr
Memory problems (how does auto ptr help?)r
OthersDetermine how you will model these exceptions in C++ and how to integrate them into your code.
5. (Research and Investigation)
There are some more issues that we would like to discuss and the reader can pursue them further by referring to the reference manuals and specifications:
r
Theexception specification; a function declaration can list those exceptions that it can throw by using an exception specification as a suffix of its declarator, for example:void getValue(int j) throw (MathErr, DataSimException, int) {
if (j == 0) throw 123;
if (j > 100) throw MatErr();
if (j < 0) throw DatasimException();
throw BoundsError(); // Throws unexpected }
This is a useful feature because it is an explicit statement of the contract between clients and servers
r
The exception handling mechanism relies on two functions for coping with errors; first, terminate()gets called when exception handling is abandoned for less subtle error handling techniques and second, unexpected() is called when a function with an ex-ception specification throws an exex-ception that is not listed in that specification. See the example above to see the circumstances under which this function is called. The function unexpected()calls terminate()r
It might be a good idea to investigate the question of exception handling in constructors and destructors.r
The ‘catch every exception’ syntax; this is a degenerate catch and throw mechanism because it is a catch-all and is used to ‘catch any exception’. It is like an insurance policy.Here is an example:
class BoundsError
{ // Empty exception class public:
};
void getValue(int j) throw (int, string) {
if (j == 0) throw int(123);
if (j > 100) throw string("out of bounds");
throw BoundsError(); // Throws unexpected if no catch(...) }
int val; cin >> val;
getValue(val);
}
catch (int& e) {
cout << "integer " << e << endl;
}
catch (string& e) {
cout << e << endl;
}
catch (...) {
cout << "No idea, really\ n:";
}
return 0;
}
r
There-throw mechanism: we have seen how to catch an exception but in the examples it was handled in the corresponding catch block. In some cases the handler cannot com-pletely handle the error. In that case the handler can then throw the exception again. In this case a re-throw is indicated by a throw without an operand. But be careful; if a re-throw is attempted when there is no exception to re-throw then the terminate() function will be called:void getValueVersionTwo(int j) throw (int, string) {
if (j == 0) throw int(123);
if (j > 100) throw string("out of bounds");
// Re-throw throw;
}
r
The memory that is allocated for the thrown exception object persists as long as there is a handler being executed for that exception6. (Exceptions and STL)
We shall discuss the Standard Template Library (STL) in Chapter 11. This library has support for exception handling and we discuss this topic here. In C++ all exceptions in the language and the STL are derived from the base class called exception. There are three main categories (Josuttis, 1999):
r
Exceptions for language support: these are part of the core language and comprise the following specific exceptions:– bad alloc: thrown when the global operator new fails – bad cast: when things go wrong in dynamic cast<T>
– bad typeid: thrown by the typeid operator
– bad exception: used to handle unexpected exceptions
r
Exception classes for STL: all exceptions are derived from the base class logic error.This has specific exception classes:
– invalid argument: this is for functions where arguments of the wrong type are used – length error: doing something that exceeds some maximum allowable size – out of range: an argument is not in the expected range, for example an index in an
array structure
– domain error: used to report a domain error.
r
Exceptions for errors outside the scope of a program: the base class is called runtime errorand it has the following specialisations:– range error: range error in some computation – overflow error: arithmetic overflow exceptions – underflow error: arithmetic underflow exceptions Here is an example to motivate the above short discussion:
try {
throw overflow error(string("first"));
}
catch (const std::runtime error& e) { // This gets caught
cout << e.what() << endl;
}
catch (const std::exception& e) {
cout << e.what() << endl;
}