5. Rewrite the Stash class from Chapter 13 of Volume 1 so that it throws out_of_range exceptions for operator[].
6. Write a generic main( ) that takes all exceptions and reports them as errors.
7. Create a class with its own operator new. This operator should allocate ten objects, and on the eleventh object "run out of memory" and throw an exception. Also add a static member function that reclaims this memory. Now create a main( ) with a try block and a catch clause that calls the memory-restoration routine. Put these inside a while loop, to demonstrate recovering from an exception and continuing execution.
8. Create a destructor that throws an exception, and write code to prove to yourself that this is a bad idea by showing that if a new exception is thrown before the handler for the existing one is reached, terminate( ) is called.
9. Prove to yourself that all exception objects (the ones that are thrown) are properly destroyed.
10. Prove to yourself that if you create an exception object on the heap and throw the pointer to that object, it will not be cleaned up.
11. Write a function with an exception specification that can throw four exception types: a char, an int, a bool, and your own exception class. Catch each in main( ) and verify the catch. Derive your exception class from a standard exception. Write the function in such a way that the system recovers and tries to execute it again.
12. Modify your solution to the exercise 8 to throw a double from the function, violating the exception specification. Catch the violation with your own unexpected handler that displays a message and exits the program gracefully (meaning abort( ) is not called).
13. Write a Garage class that has a Car that is having troubles with its Motor. Use a function-level try block in the Garage class constructor to catch an exception (thrown from the Motor class) when its Car object is initialized. Throw a different exception from the body of the Garage constructor’s handler and catch it in main( ).
2: Defensive programming
Writing "perfect software" may be an elusive Holy Grail for developers, but a few defensive techniques, routinely applied, can go a long way toward narrowing the gap between code and ideal.
Although the complexity of typical production software guarantees that testers will always have a job, chances are you still yearn to produce defect-free software. (At least we hope you do!) Object-oriented design techniques do much to corral the difficulty of large projects, to be sure. Eventually, however, you have to get down to writing loops and functions. These details of "programming in the small" become the building blocks of the implementation of larger components called for by your design efforts. If your loops are off by one or your functions calculate the correct values only "most" of the time, you’re in deep trouble no matter how fancy your overall methodology. In this chapter, we’re interested in coding practices that keep you on track toward a working solution regardless of the size of your project.
Your code is, among other things, an expression of your attempt to solve a problem. It should be clear to the reader (including yourself) exactly what you were thinking when you designed that loop. At certain points in your program, you should be able to make bold statements that some condition or other holds. (If you can’t, you really haven’t yet solved the problem.) Such statements are called
To illustrate, consider how to write a program that plays the guessing game of Hi-lo. You play this game by having one person think of a number between 1 and 100, and having the other person guess the number. (We’ll let the computer do the guessing.) The person who holds the number tells the guesser whether their guess is high, low or correct. The best strategy for the guesser is of course
bool guessed = false;
while (!guessed) {
…
}
because a malicious user might respond deceitfully, and you could spend all day guessing. What assumption, however simple, are you making each time you guess? In other words, what condition should hold