ADVANCED DECISIONS In this topic, we will cover more advanced ways for a program to choose between different actions: the else if construction, the powerful switch statement, and the weird but compact conditional operator. I'll also apply the switch statement to the hot dog stand program to enhance its functionality. The else if Construction In the previous topic, we looked at an example of an if...else statement that displayed "am" or "pm" for hour values less than and greater than 12. We also acknowledged that this statement didn't handle the occasions when hour was 0 or 12. How can we fix this? We can check for these specific values of hour and respond accordingly. Here's how that looks with an if...else ladder: if(hours == 0) // first-level indent cout << "Midnight"; else if(hours == 12) // second-level indent cout << "Noon"; else if(hours < 12) // third-level indent cout << hours << " am"; else cout << hours-12 << " pm"; If the hours is 0, it's midnight, so the program displays an appropriate message and exits from the entire if...else ladder. If it isn't 0, the program goes on to the next if...else. It keep moving to more deeply nested if...else statements until it finds one whose test expression is true or it runs out of possibilities. This arrangement does what I want, but it's hard for humans to read the multiple indentations. Here's a somewhat more easily understood way to rewrite the same code: if(hours == 0) cout << "Midnight"; else if(hours == 12) cout << "Noon"; else if(hours < 12) cout << hours << " am"; else cout << hours-12 << " pm"; The if that follows each else is simply moved up onto the same line, thus creating a sort of artificial else if construction and removing the multiple levels of indentation. This arrangement not only saves space, it presents a clearer picture of the program's logic (at least, after you've gotten used to it). Notice that the else if construction is not really a part of the syntax of the C++ language; it's merely a way to rewrite an if...else ladder by rearranging the whitespace on the page. You can do this because--as you know--the compiler doesn't care about whitespace. Fine-Tuning Loops This is a good place to introduce the break and continue statements, even though they pertain to loops, because they are used most effectively in conjunction with decisions. Also, break is an important feature in the switch statement, which I'll demonstrate next. Usually loops work well with the straightforward syntax I showed in the last session. However, sometimes you need to fudge things a bit to make a loop behave as you want. The break and continue statements provide this added flexibility. The break Statement The break statement causes you to exit immediately from a loop, as shown in Figure 2-3. Figure 2-3 Operation of the break statement The break statement is often used to handle unexpected or nonstandard situations that arise within a loop. For example, here's a code fragment that sets the variable isPrime to 1 if an integer n is a prime number or to 0 if n is not a prime number. (A prime number is divisible only by itself and 1.) To tell if n is prime, I use the straightforward approach of trying to divide it by all the numbers up to n-1. If any of them divide evenly (with no remainder), then it's not prime. isPrime = 1; // assume n is prime for(j=2; j<n; ++j) // divide by all integers from 2 to n-1 { if(n%j == 0) // if evenly divisible, { isPrime = 0; // n is not a prime break; // no point in looping again } } I want to divide by all the numbers up to n-1, so I use a for loop with appropriate expressions. However, if one of the j values does divide evenly, there's no use remaining in the loop and dividing by the remaining the j values. As soon as the program finds the first number that divides evenly, it should set isPrime to 0 and then immediately exit from the loop. The break statement allows you to exit from the loop at any time. The continue Statement The continue statement is similar to the break statement in that it is usually activated by an unexpected condition in a loop. However, it returns control to the top of the loop--causing the loop to continue--rather than causing an exit from the loop. Figure 2-4 shows how this looks. Figure 2-4 Operation of the continue statement Whereas the break statement causes an exit from a loop, the continue statement causes part of the loop to be "short-circuited" or bypassed while the loop keeps running. That is, following a continue, control goes back to the top of the loop. Here's an example: do { cout << "Enter dividend: "; cin >> dividend; cout << "Enter divisor: "; cin >> divisor; if(divisor == 0) // if user error, { cout << "Divisor can't be zero\n"; continue; // go back to top of loop } cout << "Quotient is " << dividend / divisor; cout "\nDo another (y/n)? "; cin >> ch; } while(ch != 'n'); Division by zero is illegal, so if the user enters 0 for the divisor, control goes back to the top of the loop and the program prompts for a new dividend and divisor so the user can try again. To exit from the loop, the user must answer 'n' to the "Do another" question. The switch Statement Now you're ready for perhaps the most powerful decision-making construction in C++. The switch statement checks a variable and routes program control to any of a number of different sections of code, depending on the value of the variable. Here's an example: switch(diskSpeed) { case 33: // if diskSpeed is 33 cout << "Long-playing album"; break; case 45: // if diskSpeed is 45 cout << "Single-selection"; break; case 78: // if diskSpeed is 78 cout << "Old single-selection"; break; default: // if nothing matches cout << "Unknown format"; } The switch statement consists of the keyword switch followed by a variable name in parentheses. The body of the switch statement, enclosed in braces, follows. Within the body are a number of labels, which are names followed by a colon. In a switch statement, these labels consist of the keyword case followed by a constant and then the colon. When the value of the switch variable is equal to the constant following a particular case, control will go to the statements following this case label. The above section of code prints different messages depending on the value of the diskSpeed variable. If diskSpeed is 33, control jumps to the label case 33. If diskSpeed is 45, control jumps to the label case 45, and so on. If diskSpeed doesn't match any of the cases, control jumps to the default label (or, if there is no default, falls through the bottom of the switch). Figure 2-5 shows the syntax of the switch statement and Figure 2-6 shows its operation. Figure 2-5 Syntax of the switch statement Figure 2-6 Operation of the switch statement The variable or expression used to determine which label is jumped to (diskSpeed, in this example) must be an integer or a character or must evaluate to an integer or a character. The values following the cases must be--or must evaluate to--integer or character constants. That is, you can use variable names or expressions, such as alpha, j+20, and ch+'0', as long as alpha, j, and ch already have appropriate values. Once control gets to a label, the statements following the label are executed one after the other from the label onward. In this example, the cout statement will be executed. Then what? If the break weren't there, control would continue down to the next cout statement, which is not what you want. Labels don't delimit a section of code, they merely name an entry point. The break causes control to break out of the switch entirely. Here's another example that gives the user the choice of three hot dog stands for which to record the sale of a hot dog. The user types a digit from '1' to '3', which is then used as the switch variable. cin >> choice; switch(choice) { case '1': stand1; // hot dog stand 1 sold a hot dog break; case '2': stand2; // hot dog stand 2 sold a hot dog break; case '3': stand3; // hot dog stand 3 sold a hot dog break; } The Conditional Operator The conditional operator was invented because a particular construction occurs often in C++ programs and it is nice to shorten it. Here's an example of the lengthy version of the code: if(alpha<beta) min = alpha; else min = beta; I have a variable min and I want to set it to alpha or beta, whichever is smaller. This if...else statement requires four lines of code. (I could put it all on one line, of course, but it would still be long and complicated.) However, using the conditional operator, I can shorten it to min = (alpha<beta) ? alpha : beta; The conditional operator is the only C++ operator that operates on three operands. It consists of two symbols: a question mark and a colon. First comes a test expression (with optional parentheses), then the question mark, then two values separated by the colon. If the test expression is true, the entire expression takes on the value before the colon (here it's alpha); if the test expression is false, the entire expression takes on the value following the colon (beta). Figure 2-7 shows the syntax of the conditional operator and Figure 2-8 shows its operation. Figure 2-7 Syntax of the conditional operator Figure 2-8 Operation of the conditional operator Here's another example. The statement absvalue = (n<0) ? -n : n; imitates an absolute value function. (The absolute value of a number is simply the number with any negative sign removed.) The result is -n if n is less than 0 and +n otherwise.