INTERNATIONAL EDITION Data Abstraction & Problem Solving with C++ Walls and Mirrors SIXTH EDITION Frank M. Carrano Timothy Henry
Operator Meaning Associativity Usage * multiply left expr * expr / divide left expr / expr % modulo left expr % expr + add left expr + expr - subtract left expr - expr << bitwise shift left left expr << expr >> bitwise shift right left expr >> expr < less than left expr < expr <= less than or equal to left expr <= expr > greater than left expr > expr >= greater than or equal to left expr >= expr == equal left expr == expr!= not equal left expr!= expr & bitwise AND left expr & expr ^ bitwise EXCLUSIVE OR left expr ^ expr bitwise OR left expr expr && logical AND left expr && expr logical OR left expr expr? : conditional left expr? expr : expr = assign left lvalue = expr *= multiply and assign left lvalue *= expr /= divide and assign left lvalue /= expr %= modulo and assign left lvalue %= expr += add and assign left lvalue += expr -= subtract and assign left lvalue -= expr <<= shift left and assign left lvalue <<= expr >>= shift right and assign left lvalue >>= expr &= AND and assign left lvalue &= expr = OR and assign left lvalue = expr ^= EXCLUSIVE OR and assign left lvalue ^= expr, comma left expr, expr Typically overloaded for I/O
C++ Interlude Exceptions 3 Contents C3.1 Background 252 C3.1.1 A Problem to Solve 252 C3.2 Assertions 253 C3.3 Throwing Exceptions 254 C3.4 Handling Exceptions 257 C3.4.1 Multiple catch Blocks 259 C3.4.2 Uncaught Exceptions 260 C3.5 Programmer-Defined Exception Classes 263 Prerequisites C++ Interlude 1 C++ Classes Exceptions provide a mechanism in C++ and other programming languages for interrupting program execution when errors, unusual circumstances, or other events occur. An exception is an object that signals the rest of the program that something unexpected has happened. Our code can react appropriately to the exception based on its type and what the exception can tell us via its methods. We handle the exception when we detect and react to it. Some exceptions indicate mistakes in your code. By correcting those mistakes, you avoid the exceptions and no longer have to worry about them. In fact, your final code gives no indication that an exception could occur. Furthermore, if your code is entirely correct, an exception will not occur. On the other hand, you can intentionally cause an exception. In fact, the programmers who wrote the code for the C++ Standard Library did so. At the very least, we need to know about exceptions so we can use the methods in the Standard Library. What should we do when an exception occurs? Should we ever intentionally cause an exception in our own programs, and if so, how would we do so? These are some of the questions that this interlude will answer. This knowledge will be particularly important when we implement the ADT stack in the next chapter.
252 C++ INTERLUDE 3 Exceptions C3.1 Background It would be great if every time a method was called, its preconditions were met. As you have seen, that does not always happen. The client could ask a method to remove an item from a container, but the container might be empty. How do we deal with this unusual or exceptional condition? In our implementations so far, a method returns false to indicate that it is unable to perform its task. This value tells the client that some precondition was not met, or that the method has failed for some other reason. It is then the client s responsibility to check the return value to ensure that the method completed its task before continuing. Sometimes it is not possible for a method to return a boolean value when an unusual situation prevents it from completing its task. For example, the ADT stack s method peek returns a copy of the top item on the stack. Its prototype is virtual ItemType peek() const = 0; What should this method do if the stack is empty? Returning a boolean value would be possible only if the stack contained boolean values, that is, if ItemType was of type bool. Regardless of the type of data in the stack, the return value will indicate a problem only if it is a special value not contained in the stack. We need a consistent mechanism to let the client know that the method could not perform its task one that does not depend on the type of data stored in the stack. Before we get into the details of such mechanisms, let s look at another example. C3.1.1 A Problem to Solve Recall from the previous C++ Interludes the video game that we are working on for our friend. Our next task is to create a function that searches for a given string in a number of boxes. The function has three parameters: an array of PlainBox<string> objects, an integer that represents the number of PlainBox objects in the array, and the string to be located in each box. The function will return a copy 1 of the box that contains the string. Listing C3-1 shows our first attempt at this function. LISTING C3-1 First try at the function findbox PlainBox<string> findbox(plainbox<string> boxes[], int size, string target) int index = 0; bool found = false ; while (!found && (index < size)) if (target == boxes[index].getitem()) found = true ; else index++; } // end while return boxes[index]; } // end findbox 1 Recall that the return statement returns a copy of its argument.
Assertions 253 This version of findbox meets the basic requirements: It searches the array of boxes and returns the box containing the target string. We encounter a problem if a box containing the target string is not in the array. If the target is not found, the function s while loop ends when index is equal to size, which is the number of entries in the array. The function would then return boxes[size], which is undefined. Problems will occur when the client tries to use this box. We can repair our code by testing found to make sure we found the target before returning a box from the array. Using a simple if statement is insufficient, as we still would have the problem of what to return when the target is not found. The next section gives one way to fix our code. C3.2 Assertions Chapter 3 and Appendix B define an assertion as a statement of truth about some aspect of a program s logic. You can express an assertion either as a comment or by using the C++ function assert. By using assert, you can make assertions about variables and objects in a program. Such assertions are in the form of a boolean expression that should be true at a specific point in the program. If the assertion is false, the assert function halts execution of the program. Assertions can be used to validate method preconditions before trying to execute the body of a function or method. To use assert in a program, you first must include its header file: #include <cassert> To call the assert function, you provide a boolean condition as its argument: assert( somebooleancondition ); When program execution reaches this statement, the boolean condition that is, the assertion is tested. If the assertion is true, the program continues to execute normally. If the assertion is false, the program halts and an error message is displayed. The assert statement is an easy way to verify a method s preconditions or postconditions. For example, when implementing the stack method peek, we could execute assert (!isempty()); before trying to return the top of the stack. Our findbox function can test whether the box was found before returning boxes[index] by calling assert : Use an assert statement to test a precondition or postcondition assert(found); If the target is not found, the assertion is false and the program halts. This prevents the program from trying to use a box that does not exist. Listing C3-2 shows the revised findbox function using assertions. LISTING C3-2 Revised findbox function with assertions PlainBox<string> findbox(plainbox<string> boxes[], int size, string target) int index = 0; bool found = false ; (continues)
254 C++ INTERLUDE 3 Exceptions while (!found && (index < size)) if (target == boxes[index].getitem()) found = true ; else index++; } // end while assert(found); // Verify that there is a box to return return boxes[index]; } // end findbox Assertions are good to use when you are testing and debugging your program. They can prevent the execution of any function or method if the preconditions have not been met. Halting your program as soon as a problematic condition becomes evident is a good debugging technique that narrows the focus of your search for errors. As useful as they are, assertions may not be the best solution for a final shipping program. A violation of a function s preconditions may be a simple mistake that the client can fix. Such a situation does not warrant terminating program execution. We need another way to let the client know that an error or unusual condition has occurred and permit it to fix or handle the situation. We should use exceptions here. Programming Tip: A statement of truth about some aspect of a program s logic is known as an assertion. You can express an assertion either as a comment or by using the assert function. By including assertions in your program, you facilitate the debugging process. VideoNote C++ exceptions C3.3 Throwing Exceptions The examples we ve looked at in this C++ Interlude have a common problem how to let the client know that an error, unusual circumstance, or event occurred during execution. The two solutions presented so far returning a boolean value or using assert to halt the program are vastly different techniques. Returning a boolean value requires that the client check the return value of the function to see whether the function was successful. We have no way to force the client to do so. If the client fails to check the return value, execution could continue even though the function did not complete its task. Even if the client checks the return value and finds that the function was unsuccessful, the client has no information about why the function failed. The client knows only that the function failed. A function that uses assertions can alleviate the need for the client to check whether it completed successfully. With assertions, the program halts when there is an error, and the client cannot perform any further tasks. For an error that either is unimportant to the client s goals or is simple for the client to fix, assertions are an extreme solution. However, an alternate way of communicating or returning information to a function s client is to throw an exception. A thrown exception bypasses normal execution, and control immediately returns to the client. The exception can contain information about the error or unusual condition that helps the client resolve the issue and possibly try the function again. You can throw an exception by executing a throw statement with the following form: throw ExceptionClass (stringargument );