- 2.1. Our emphasis
- 2.2. The basic goal—a major difference between C++ and Java
- 2.3. Constructors and destructor
- 2.4. Operator overloading in C++
- 2.5. Operator overloading in Java
- 2.6. Flow-control constructs
- 2.7. Manipulating character strings in C++
- 2.8. Canonical class structure
- 2.9. Overcoming macrophobia
- 2.10. Program readability
- 2.11. Error detection and exceptions
2.6. Flow-control constructs
You undoubtedly already know how all the flow-control constructs work. In this section, we’re just going to recommend two good practices.
2.6.1. Prefer prefix increment and decrement
When the increment or decrement operators are used in a separate statement or clause, we don’t care whether the operation is performed before or after the value is returned—we don’t use the value. For example, many C programmers habitually use and many textbooks recommend this loop-control idiom:
for (ctr=0; ctr < limit; ctr++);
If you have that habit, you should change it to
for (ctr=0; ctr < limit; ++ctr);
That makes no difference, of course, when ctr is a built-in primitive data item, such as an int. It makes a big difference, however, when ctr is an object of a defined class, such as Date. We know that the postfix version of the overloaded ++ operator creates a new object, while the prefix version doesn’t (see Section 2.4.3). The postfix version’s extra overhead in execution time may or may not be significant, but if the program executes it billions of times, it may have a noticeable impact. Anyway, there’s no trade-off, since it costs nothing to use the prefix version.
2.6.2. Avoid switch case for implementing a table
Some introductory textbooks illustrate the switch-case construct with a misguided example like this one:
switch(monthNumber) { case 1: ndays = 31; break; case 2: ndays = 28; break; case 3: ndays = 31; break; case 4: ndays = 30; break; . . case 12: ndays = 31; break; default: ndays = 0;// (error) }
Or it is illustrated as a function (break not needed):
int daysInMonth(const short monthNo) {switch(monthNo) { case 1: return 31; case 2: return 28; case 3: return 31; case 4: return 30; . . case 12: return 31; } return(0);// error }
What’s wrong with that? Well, all the code is doing is implementing a simple table. The following function does the same thing more clearly and probably more efficiently:
int daysInMonth(const short monthNo) {static const short ndays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; if (monthNo >=1 && monthNo <=12) return ndays[monthNumber-1]; return 0; }
The intent of switch case is to support doing something different depending on the value of some number, usually an enumerated data item. For example, a generalized transaction processor might contain something like this:
switch(transactionType) { case addNewCustomer: . case changeAddress: . .
However, whenever you find yourself doing the same thing, such as return or assigning different values to the same variable, you should consider simplifying your logic with an explicit table.
Problems and exercises
2.6-1Explain in what practical respects the second version of daysInMonth above is better than the first one (switch case).
2.6-2 Find an example in a C, C++, or Java textbook where the author has used switch case to do the same thing with different values. Rewrite it to use a straightforward table.