switch Multiple-Selection Statement
We discussed the if single-selection statement and the if...else double-selection statement in Chapter 4. C++ provides the switch multiple-selection statement to perform many different actions based on the possible values of a variable or expression. Each action is associated with the value of a constant integral expression (i.e., any combination of character constants and integer constants that evaluates to a constant integer value) that the variable or expression on which the switch is based may assume.
GradeBook Class with switch Statement to Count A, B, C, D and F Grades
In the next example, we present an enhanced version of the GradeBook class introduced in Chapter 3 and further developed in Chapter 4. The new version of the class asks the user to enter a set of letter grades, then displays a summary of the number of students who received each grade. The class uses a switch to determine whether each grade entered is an A, B, C, D or F and to increment the appropriate grade counter. Class GradeBook is defined in Fig. 5.9, and its member-function definitions appear in Fig. 5.10. Figure 5.11 shows sample inputs and outputs of the main program that uses class GradeBook to process a set of grades.
Figure 5.9. GradeBook class definition.
1 // Fig. 5.9: GradeBook.h 2 // Definition of class GradeBook that counts A, B, C, D and F grades. 3 // Member functions are defined in GradeBook.cpp 4 5 #include // program uses C++ standard string class 6 using std::string; 7 8 // GradeBook class definition 9 class GradeBook 10 { 11 public: 12 GradeBook( string ); // constructor initializes course name 13 void setCourseName( string ); // function to set the course name 14 string getCourseName(); // function to retrieve the course name 15 void displayMessage(); // display a welcome message 16 void inputGrades(); // input arbitrary number of grades from user 17 void displayGradeReport(); // display a report based on the grades 18 private: 19 string courseName; // course name for this GradeBook 20 int aCount; // count of A grades 21 int bCount; // count of B grades 22 int cCount; // count of C grades 23 int dCount; // count of D grades 24 int fCount; // count of F grades 25 }; // end class GradeBook |
Figure 5.10. GradeBook class uses switch statement to count letter grades A, B, C, D and F.
(This item is displayed on pages 201 - 203 in the print version)
1 // Fig. 5.10: GradeBook.cpp 2 // Member-function definitions for class GradeBook that 3 // uses a switch statement to count A, B, C, D and F grades. 4 #include 5 using std::cout; 6 using std::cin; 7 using std::endl; 8 9 #include "GradeBook.h" // include definition of class GradeBook 10 11 // constructor initializes courseName with string supplied as argument; 12 // initializes counter data members to 0 13 GradeBook::GradeBook( string name ) 14 { 15 setCourseName( name ); // validate and store courseName 16 aCount = 0; // initialize count of A grades to 0 17 bCount = 0; // initialize count of B grades to 0 18 cCount = 0; // initialize count of C grades to 0 19 dCount = 0; // initialize count of D grades to 0 20 fCount = 0; // initialize count of F grades to 0 21 } // end GradeBook constructor 22 23 // function to set the course name; limits name to 25 or fewer characters 24 void GradeBook::setCourseName( string name ) 25 { 26 if ( name.length() <= 25 ) // if name has 25 or fewer characters 27 courseName = name; // store the course name in the object 28 else // if name is longer than 25 characters 29 { // set courseName to first 25 characters of parameter name 30 courseName = name.substr( 0, 25 ); // select first 25 characters 31 cout << "Name "" << name << "" exceeds maximum length (25). " 32 << "Limiting courseName to first 25 characters. " << endl; 33 } // end if...else 34 } // end function setCourseName 35 36 // function to retrieve the course name 37 string GradeBook::getCourseName() 38 { 39 return courseName; 40 } // end function getCourseName 41 42 // display a welcome message to the GradeBook user 43 void GradeBook::displayMessage() 44 { 45 // this statement calls getCourseName to get the 46 // name of the course this GradeBook represents 47 cout << "Welcome to the grade book for " << getCourseName() << "! " 48 << endl; 49 } // end function displayMessage 50 51 // input arbitrary number of grades from user; update grade counter 52 void GradeBook::inputGrades() 53 { 54 int grade; // grade entered by user 55 56 cout << "Enter the letter grades." << endl 57 << "Enter the EOF character to end input." << endl; 58 59 // loop until user types end-of-file key sequence 60 while ( ( grade = cin.get() ) != EOF ) 61 { 62 // determine which grade was entered 63 switch ( grade ) // switch statement nested in while 64 { 65 case 'A': // grade was uppercase A 66 case 'a': // or lowercase a 67 aCount++; // increment aCount 68 break; // necessary to exit switch 69 70 case 'B': // grade was uppercase B 71 case 'b': // or lowercase b 72 bCount++; // increment bCount 73 break; // exit switch 74 75 case 'C': // grade was uppercase C 76 case 'c': // or lowercase c 77 cCount++; // increment cCount 78 break; // exit switch 79 80 case 'D': // grade was uppercase D 81 case 'd': // or lowercase d 82 dCount++; // increment dCount 83 break; // exit switch 84 85 case 'F': // grade was uppercase F 86 case 'f': // or lowercase f 87 fCount++; // increment fCount 88 break; // exit switch 89 90 case ' ': // ignore newlines, 91 case ' ': // tabs, 92 case ' ': // and spaces in input 93 break; // exit switch 94 95 default: // catch all other characters 96 cout << "Incorrect letter grade entered." 97 << " Enter a new grade." << endl; 98 break; // optional; will exit switch anyway 99 } // end switch 100 } // end while 101 } // end function inputGrades 102 103 // display a report based on the grades entered by user 104 void GradeBook::displayGradeReport() 105 { 106 // output summary of results 107 cout << " Number of students who received each letter grade:" 108 << " A: " << aCount // display number of A grades 109 << " B: " << bCount // display number of B grades 110 << " C: " << cCount // display number of C grades 111 << " D: " << dCount // display number of D grades 112 << " F: " << fCount // display number of F grades 113 << endl; 114 } // end function displayGradeReport |
Figure 5.11. Creating a GradeBook object and calling its member functions.
(This item is displayed on pages 206 - 207 in the print version)
1 // Fig. 5.11: fig05_11.cpp 2 // Create GradeBook object, input grades and display grade report. 3 4 #include "GradeBook.h" // include definition of class GradeBook 5 6 int main() 7 { 8 // create GradeBook object 9 GradeBook myGradeBook( "CS101 C++ Programming" ); 10 11 myGradeBook.displayMessage(); // display welcome message 12 myGradeBook.inputGrades(); // read grades from user 13 myGradeBook.displayGradeReport(); // display report based on grades 14 return 0; // indicate successful termination 15 } // end main
|
Like earlier versions of the class definition, the GradeBook class definition (Fig. 5.9) contains function prototypes for member functions setCourseName (line 13), getCourseName (line 14) and displayMessage (line 15), as well as the class's constructor (line 12). The class definition also declares private data member courseName (line 19).
Class GradeBook (Fig. 5.9) now contains five additional private data members (lines 2024)counter variables for each grade category (i.e., A, B, C, D and F). The class also contains two additional public member functionsinputGrades and displayGradeReport. Member function inputGrades (declared in line 16) reads an arbitrary number of letter grades from the user using sentinel-controlled repetition and updates the appropriate grade counter for each grade entered. Member function displayGradeReport (declared in line 17) outputs a report containing the number of students who received each letter grade.
Source-code file GradeBook.cpp (Fig. 5.10) contains the member-function definitions for class GradeBook. Notice that lines 1620 in the constructor initialize the five grade counters to 0when a GradeBook object is first created, no grades have been entered yet. As you will soon see, these counters are incremented in member function inputGrades as the user enters grades. The definitions of member functions setCourseName, getCourseName and displayMessage are identical to those found in the earlier versions of class GradeBook. Let's consider the new GradeBook member functions in detail.
Reading Character Input
The user enters letter grades for a course in member function inputGrades (lines 52101). Inside the while header, at line 60, the parenthesized assignment ( grade = cin.get() ) executes first. The cin.get() function reads one character from the keyboard and stores that character in integer variable grade (declared in line 54). Characters normally are stored in variables of type char; however, characters can be stored in any integer data type, because they are represented as 1-byte integers in the computer. Thus, we can treat a character either as an integer or as a character, depending on its use. For example, the statement
cout << "The character (" << 'a' << ") has the value " << static_cast< int > ( 'a' ) << endl;
prints the character a and its integer value as follows:
The character (a) has the value 97
The integer 97 is the character's numerical representation in the computer. Most computers today use the ASCII (American Standard Code for Information Interchange) character set, in which 97 represents the lowercase letter 'a'. A table of the ASCII characters and their decimal equivalents is presented in Appendix B.
Assignment statements as a whole have the value that is assigned to the variable on the left side of the =. Thus, the value of the assignment expression grade = cin.get() is the same as the value returned by cin.get() and assigned to the variable grade.
The fact that assignment statements have values can be useful for assigning the same value to several variables. For example,
a = b = c = 0;
first evaluates the assignment c = 0 (because the = operator associates from right to left). The variable b is then assigned the value of the assignment c = 0 (which is 0). Then, the variable a is assigned the value of the assignment b = (c = 0) (which is also 0). In the program, the value of the assignment grade = cin.get() is compared with the value of EOF (a symbol whose acronym stands for "end-of-file"). We use EOF (which normally has the value 1) as the sentinel value. However, you do not type the value 1, nor do you type the letters EOF as the sentinel value. Rather, you type a system-dependent keystroke combination that means "end-of-file" to indicate that you have no more data to enter. EOF is a symbolic integer constant defined in the header file. If the value assigned to grade is equal to EOF, the while loop (lines 60100) terminates. We have chosen to represent the characters entered into this program as ints, because EOF has an integer value.
On UNIX/Linux systems and many others, end-of-file is entered by typing
d
on a line by itself. This notation means to press and hold down the Ctrl key, then press the d key. On other systems such as Microsoft Windows, end-of-file can be entered by typing
z
[Note: In some cases, you must press Enter after the preceding key sequence. Also, the characters ^Z sometimes appear on the screen to represent end-of-file, as is shown in Fig. 5.11.]
Portability Tip 5.2
The keystroke combinations for entering end-of-file are system dependent. |
Portability Tip 5.3
Testing for the symbolic constant EOF rather than 1 makes programs more portable. The ANSI/ISO C standard, from which C++ adopts the definition of EOF, states that EOF is a negative integral value (but not necessarily 1), so EOF could have different values on different systems. |
In this program, the user enters grades at the keyboard. When the user presses the Enter (or Return) key, the characters are read by the cin.get() function, one character at a time. If the character entered is not end-of-file, the flow of control enters the switch statement (lines 6399), which increments the appropriate letter-grade counter based on the grade entered.
switch Statement Details
The switch statement consists of a series of case labels and an optional default case. These are used in this example to determine which counter to increment, based on a grade. When the flow of control reaches the switch, the program evaluates the expression in the parentheses (i.e., grade) following keyword switch (line 63). This is called the controlling expression. The switch statement compares the value of the controlling expression with each case label. Assume the user enters the letter C as a grade. The program compares C to each case in the switch. If a match occurs (case 'C': at line 75), the program executes the statements for that case. For the letter C, line 77 increments cCount by 1. The break statement (line 78) causes program control to proceed with the first statement after the switchin this program, control transfers to line 100. This line marks the end of the body of the while loop that inputs grades (lines 60100), so control flows to the while's condition (line 60) to determine whether the loop should continue executing.
The cases in our switch explicitly test for the lowercase and uppercase versions of the letters A, B, C, D and F. Note the cases at lines 6566 that test for the values 'A' and 'a' (both of which represent the grade A). Listing cases consecutively in this manner with no statements between them enables the cases to perform the same set of statementswhen the controlling expression evaluates to either 'A' or 'a', the statements at lines 6768 will execute. Note that each case can have multiple statements. The switch selection statement differs from other control statements in that it does not require braces around multiple statements in each case.
Without break statements, each time a match occurs in the switch, the statements for that case and subsequent cases execute until a break statement or the end of the switch is encountered. This is often referred to as "falling through" to the statements in subsequent cases. (This feature is perfect for writing a concise program that displays the iterative song "The Twelve Days of Christmas" in Exercise 5.28.)
Common Programming Error 5.8
Forgetting a break statement when one is needed in a switch statement is a logic error. |
Common Programming Error 5.9
Omitting the space between the word case and the integral value being tested in a switch statement can cause a logic error. For example, writing case3: instead of writing case 3: simply creates an unused label. We will say more about this in Appendix E, C Legacy Code Topics. In this situation, the switch statement will not perform the appropriate actions when the switch's controlling expression has a value of 3. |
Providing a default Case
If no match occurs between the controlling expression's value and a case label, the default case (lines 9598) executes. We use the default case in this example to process all controlling-expression values that are neither valid grades nor newline, tab or space characters (we discuss how the program handles these white-space characters shortly). If no match occurs, the default case executes, and lines 9697 print an error message indicating that an incorrect letter grade was entered. If no match occurs in a switch statement that does not contain a default case, program control simply continues with the first statement after the switch.
Good Programming Practice 5.10
Provide a default case in switch statements. Cases not explicitly tested in a switch statement without a default case are ignored. Including a default case focuses the programmer on the need to process exceptional conditions. There are situations in which no default processing is needed. Although the case clauses and the default case clause in a switch statement can occur in any order, it is common practice to place the default clause last. |
Good Programming Practice 5.11
In a switch statement that lists the default clause last, the default clause does not require a break statement. Some programmers include this break for clarity and for symmetry with other cases. |
Ignoring Newline, Tab and Blank Characters in Input
Note that lines 9093 in the switch statement of Fig. 5.10 cause the program to skip newline, tab and blank characters. Reading characters one at a time can cause some problems. To have the program read the characters, we must send them to the computer by pressing the Enter key on the keyboard. This places a newline character in the input after the character we wish to process. Often, this newline character must be specially processed to make the program work correctly. By including the preceding cases in our switch statement, we prevent the error message in the default case from being printed each time a newline, tab or space is encountered in the input.
Common Programming Error 5.10
Not processing newline and other white-space characters in the input when reading characters one at a time can cause logic errors. |
Testing Class GradeBook
Figure 5.11 creates a GradeBook object (line 9). Line 11 invokes the object's displayMessage member function to output a welcome message to the user. Line 12 invokes the object's inputGrades member function to read a set of grades from the user and keep track of the number of students who received each grade. Note that the input/output window in Fig. 5.11 shows an error message displayed in response to entering an invalid grade (i.e., E). Line 13 invokes GradeBook member function displayGradeReport (defined in lines 104114 of Fig. 5.10), which outputs a report based on the grades entered (as in the output in Fig. 5.11).
switch Statement UML Activity Diagram
Figure 5.12 shows the UML activity diagram for the general switch multiple-selection statement. Most switch statements use a break in each case to terminate the switch statement after processing the case. Figure 5.12 emphasizes this by including break statements in the activity diagram. Without the break statement, control would not transfer to the first statement after the switch statement after a case is processed. Instead, control would transfer to the next case's actions.
Figure 5.12. switch multiple-selection statement UML activity diagram with break statements.
(This item is displayed on page 208 in the print version)
The diagram makes it clear that the break statement at the end of a case causes control to exit the switch statement immediately. Again, note that (besides an initial state, transition arrows, a final state and several notes) the diagram contains action states and decisions. Also, note that the diagram uses merge symbols to merge the transitions from the break statements to the final state.
Imagine, again, that the programmer has a bin of empty switch statement UML activity diagramsas many as the programmer might need to stack and nest with the activity diagrams of other control statements to form a structured implementation of an algorithm. The programmer fills in the action states and decision symbols with action expressions and guard conditions appropriate to the algorithm. Note that, although nested control statements are common, it is rare to find nested switch statements in a program.
When using the switch statement, remember that it can be used only for testing a constant integral expressionany combination of character constants and integer constants that evaluates to a constant integer value. A character constant is represented as the specific character in single quotes, such as 'A'. An integer constant is simply an integer value. Also, each case label can specify only one constant integral expression.
Common Programming Error 5.11
Specifying an expression including variables (e.g., a + b) in a switch statement's case label is a syntax error. |
Common Programming Error 5.12
Providing identical case labels in a switch statement is a compilation error. Providing case labels containing different expressions that evaluate to the same value also is a compilation error. For example, placing case 4 + 1: and case 3 + 2: in the same switch statement is a compilation error, because these are both equivalent to case 5:. |
In Chapter 13, we present a more elegant way to implement switch logic. We will use a technique called polymorphism to create programs that are often clearer, more concise, easier to maintain and easier to extend than programs that use switch logic.
Notes on Data Types
C++ has flexible data type sizes (see Appendix C, Fundamental Types). Different applications, for example, might need integers of different sizes. C++ provides several data types to represent integers. The range of integer values for each type depends on the particular computer's hardware. In addition to the types int and char, C++ provides the types short (an abbreviation of short int) and long (an abbreviation of long int). The minimum range of values for short integers is 32,768 to 32,767. For the vast majority of integer calculations, long integers are sufficient. The minimum range of values for long integers is 2,147,483,648 to 2,147,483,647. On most computers, ints are equivalent either to short or to long. The range of values for an int is at least the same as that for short integers and no larger than that for long integers. The data type char can be used to represent any of the characters in the computer's character set. It also can be used to represent small integers.
Portability Tip 5.4
Because ints can vary in size between systems, use long integers if you expect to process integers outside the range 32,768 to 32,767 and you would like to run the program on several different computer systems. |
Performance Tip 5.3
If memory is at a premium, it might be desirable to use smaller integer sizes. |
Performance Tip 5.4
Using smaller integer sizes can result in a slower program if the machine's instructions for manipulating them are not as efficient as those for the natural-size integers, i.e., integers whose size equals the machine's word size (e.g., 32 bits on a 32-bit machine, 64 bits on a 64-bit machine). Always test proposed efficiency "upgrades" to be sure they really improve performance. |