C++ From Scratch

Contents


3

Program Flow


In this Chapter

This chapter takes a look at how programs progress, how loops are created, and how programs branch based on user input and other conditions.

Building Robustness

Listing 2.1 in Chapter 2, "Getting Started," is vulnerable to incorrect user input. For example, what happens if the user asks for 35 letters or says, "Use five letters in six positions without duplicates." This, of course, is impossible: You can't put five letters into six positions without duplicating at least one letter. How do you prevent the user from giving you bogus data?

A robust program is one that can handle any user input without crashing. Writing highly robust code is difficult and complex. Therefore, we won't set a goal of handling absolutely any bogus data, but we will attempt to deal with a few highly predictable errors.


Robust--A program is robust when it can handle incorrect user input or other unanticipated events without crashing.


Listing 3.1 illustrates a more complex version of the code you just considered. Before it is discussed, take a look at it and see if you can make some guesses about what it is doing.

Listing 3.1[em]A More Complex Version of Decryptix!

0:  #include <iostream>
1:  using namespace std;
2:  int main()
3:  {
4:    cout << "Decryptix. Copyright 1999 Liberty";
5:    cout << "Associates, Inc. Version 0.2\n\n" << endl;
6:    cout << "There are two ways to play Decryptix: either";
7:    cout << "you can guess a pattern I create,\n";
8:    cout << "or I can guess your pattern.\n\n";
9:    cout << "If you are guessing, I will think of a pattern\n";
10:   cout << "of letters (e.g., abcde).\n\n";
11:   cout << "On each turn, you guess the pattern and I will\n";
12:   cout << "tell you how many letters you got right, and how many\n";
13:   cout << "of the correct letters were in the correct position.\n\n";
14:   cout << "The goal is to decode the puzzle as quickly as\n";
15:   cout << "possible. You control how many letters can be\n";
16:   cout << "used and how many positions (e.g., 5 possible \n";
17:   cout << "letters in 4 positions) as well as whether or not\n";
18:   cout << "the pattern might contain duplicate \n";
19:   cout << "letters (e.g., aabcd).\n\n";
20:   cout << "If I'm guessing, you think of a pattern and score \n";
21:   cout << "each of my answers.\n\n" << endl;
22:  
23:  
24:    int round = 1;
25:    int howManyLetters = 0, howManyPositions = 0;
26:    bool duplicatesAllowed = false;
27:    bool valid = false;
28:  
29:    const int minLetters = 2;
30:    const int maxLetters = 10;
31:    const int minPositions = 3;
32:    const int maxPositions = 10;
33:  
34:  
35:    while ( ! valid )
36:    {
37:       while ( howManyLetters < minLetters 
38:          || howManyLetters > maxLetters )
39:       {
40:          cout << "How many letters? (";
41:          cout << minLetters << "-" << maxLetters << "): ";
42:          cin >> howManyLetters;
43:          if ( howManyLetters < minLetters 
44:             || howManyLetters > maxLetters )
45:          {
46:             cout << "please enter a number between "; 
47:             cout << minLetters << " and " << maxLetters << endl;
48:          }
49:       }
50:  
51:       while ( howManyPositions < minPositions 
52:          || howManyPositions > maxPositions )
53:       {
54:          cout << "How many positions? (";
55:          cout << minPositions << "-" << maxPositions << "): ";
56:          cin >> howManyPositions;
57:          if ( howManyPositions < minPositions 
58:             || howManyPositions > maxPositions )
59:          {
60:             cout << "please enter a number between ";
61:             cout << minPositions <<" and " << maxPositions << endl;
62:          }
63:       }
64:  
65:       char choice = ' ';
66:       while ( choice != 'y' && choice != 'n' )
67:       {
68:          cout << "Allow duplicates (y/n)? ";
69:          cin >> choice;
70:       }
71:  
72:       duplicatesAllowed = choice == 'y' ? true : false;
73:  
74:       if ( ! duplicatesAllowed 
75:          && howManyPositions > howManyLetters )
76:       {
77:         cout << "I can't put " << howManyLetters;
78:         cout << " letters in " << howManyPositions;
79:         cout << " positions without duplicates! Please try again.\n";
80:          howManyLetters = 0;
81:          howManyPositions = 0;
82:       }
83:       else
84:       valid = true;
85:    }
86:  
87:    return 0;
88:  }

What Are You Trying to Accomplish?

Line 35 brings us to the first line of code after the initialization of the local variables and constants.

The goal with this piece of code is to prompt the user for a series of pieces of information. Specifically, you want to know how many letters he or she will use (for example, five letters means a, b, c, d, and e), how many positions (for example, three positions means there are three letters that are actually used in the code), and whether you'll allow duplicates (can one letter repeat?)

The problem is that the user might not give you valid information. For example, the user might tell you to use four letters in five positions with no duplicates. This, unfortunately, is not physically possible. You want to make sure that you have reasonable choices before moving forward with the program.


NOTE: Let me pause and point out that in a real commercial program, it is not unusual for literally dozens--or even hundreds--of lines of code to be devoted to catching and responding appropriately to bogus user input. You will not endeavor to be quite that robust here, but you do want to trap the obvious mistakes and ask the user to try again.


Solving the Problem with Loops

The essential approach to solving this problem is to do some work (ask the user for input), test a condition (determine whether the data makes sense), and, if the condition fails, start over.

This is called a loop; C++ supports a number of different looping mechanisms.


loop--A section of code that repeats.


Remember that you've created two constant integers for the values you need: minLetters and maxLetters. Because you initialized howManyLetters to zero, when you start out, howManyLetters is of course less than minLetters, assuming that minLetters is greater than zero.

You want to continue to prompt and then reprompt the user while howManyLetters is either less than the minimum or more than the maximum. To do this, you'll create a while loop.

The syntax for the while statement is as follows:

while ( condition )
statement;

condition is any C++ expression, and statement is any valid C++ statement or block of statements. When condition evaluates to true, statement executes, and then condition is tested again. This continues until condition tests false, at which time the while loop terminates and execution continues on the first line following statement.

Your while statement might be

while ( howManyLetters < minLetters )
{
     //...
}

NOTE: The symbol

//...

indicates that I've left out code that you're not considering at the moment.


Relational Operators

Relational operators determine whether two numbers are equal, or whether one is greater or less than the other. Every relational statement evaluates to either true or false.


Relational Operator--A symbol (for example, > or <) that is used to determine the relative size of two objects. Relational operators evaluate to true or false.


If the integer variable howManyLetters has the value 1 and the constant minLetters has the value 2, the expression

howManyLetters < minLetters

returns true.

A while loop continues while the expression is true, so if howManyLetters is 1 and minLetters is 2, the expression is true and the while loop will in fact execute.

Note that I talk about a single statement executing. It is also possible to execute a block (that is, a group) of statements.

Blocks and Compound Statements

A statement can be a single line or it can be a block of code that is surrounded by braces, which is treated as a single statement. Although every statement in the block must end with a semicolon, the block itself does not end with a semicolon.

The block of code itself can consist of any number of statements, but it is treated as a single statement.

This enables you to run several statements as the execution of a single while loop.

Not only can you test whether one variable is less than another, you can test whether one is larger, or even whether they are the same.

There are six relational operators listed in Table 3.1, which also shows each operator's use and some sample code.

Table 3.1 Relational Operators

Name

Operator

Sample

Evaluates

Equals

==

100 == 50;

false



50 == 50;

true

Not Equals

!=

100 != 50;

true



50 != 50;

false

Greater Than

>

100 > 50;

true



50 > 50;

false

Greater Than

>=

100 >= 50;

true

or Equals


50 >= 50;

true

Less Than

<

100 < 50;

false



50 < 50;

false

Less Than

<=

100 <= 50;

false

or Equals


50 <= 50;

true


WARNING: Many novice C++ programmers confuse the assignment operator (=) with the equals operator (==). This can create a nasty bug in your program.


Logical Operators

The problem with this while loop is that it tests only whether howManyLetters is less than the constant minLetters; you also need to test to find out whether howManyLetters is greater than maxLetters.

You can test them separately:

while ( howManyLetters < minLetters )
{
     //...
}
while ( howManyLetters > maxLetters )
{
     //...
}

This will work, but the code within both while loops will be identical. In fact, what you are really trying to say is that you want to repeat this work while howManyLetters is less than minLetters or while howManyLetters is greater than maxLetters. C++ enables you to make exactly that test using the logical OR operator (||). You create the logical OR operator by pressing Shift+\ twice.

The Logical OR Operator

In this case, you are asking for the while loop to continue as long as either condition is true, so you use logical OR:

while ( howManyLetters < minLetters || howManyLetters > maxLetters )
{
     //...
}

This code says that the statement (between the braces) is executed if it is true that howManyLetters is less than minLetters or if it is true that howManyLetters is greater than maxLetters (or if both conditions are true).

The Logical AND Operator

At other times, you might want to continue only if both conditions are true, in which case you want to use while (condition 1 and condition 2). The logical AND operator (&&) handles this condition. A logical AND statement evaluates two expressions, and if both expressions are true, the logical AND statement is true as well.

For example, you can test the following:

while ( howManyLetters > minLetters && howManyLetters < maxLetters )
{
     //...
}

this statement executes only if it is true that howManyLetters is greater than minLetters and if it is also true that howManyLetters is less than maxLetters.


Logical operator OR--|| created by two vertical lines, by pressing Shift+backslash (\) twice.

Logical operator AND--&& created by two ampersands, by pressing Shift+7 twice.


The if StatementAn if statement allows you to take action only if a condition is true (and to skip the action or do something else if the condition is false). You use if statements every day:

If it is raining, I'll take my umbrella.

If I have time, I'll walk the dog.

If I don't walk the dog, I'll be sorry.

The simplest form of an if statement is this:

if (expression)
     statement;

The expression in the parentheses can be any expression at all, but it usually contains one of the relational expressions. If the expression has the value false, the statement is skipped. If it evaluates to true, the statement executes. Once again, the statement can certainly be a compound statement between braces, as you see here.

The Logical NOT Operator

A logical NOT statement (!) evaluates true if the expression that is being tested is false. This is confusing at first, but an example will help. I might start by saying, "If it is raining, I'll bring my umbrella":

if ( raining )
  BringUmbrella();

How do I express that I'll only go for a walk if it is not raining?

if ( ! raining )
   GoForWalk();

I can also reverse these:

if ( ! raining )
  LeaveUmbrella;
if ( raining )
  GoForWalk;

You get the idea.

Thus

if ( ! valid )

is true only if valid is false.


Logical NOT--Evalutes true when something is not true, and false when it is true.


You can use this nifty construct to turn your logical OR statement into a logical AND statement without changing its meaning. For example

while ( howManyLetters < minLetters || howManyLetters > maxLetters )

is exactly the same thing as

while ( (! (howManyLetters > minLetters) ) && 
(! (howManyLetters > maxLetters ) _) )

The logic of this is easy to understand if you use values. Assume that minLetters is 2, maxLetters is 10, and howManyLetters is 0.

In that case, the while loop executes because the left part of the statement is true (0 is less than 2). In an OR statement, only one side must be true for the entire statement to return true.

Thus,

while ( howManyLetters < minLetters || howManyLetters > maxLetters )

becomes

while ( 0 < 2 || 0 > 10 ) // substitute the values

becomes

while ( true || false ) // evaluate the truth of each side

becomes

while ( true ) // if either is true, the statement is true

The second statement,

while ( (! (howManyLetters > minLetters) ) &&
(! (howManyLetters > maxLetters ) ) )

becomes

while ( (! (0 > 2) ) && (! (0 > 10 ) _) )

Now each side must be evaluated. The NOT symbol reverses the truth of what follows. It is as if this said, "While it is not true that zero is greater than 2 and it is not true that zero is greater than 10."

Thus you get

while ( (! (false) ) && (! (false ) _) )

When you apply NOT to false, you get true:

while ( (true) ) && (true) )

With an AND statement, both sides must be true; in this case they are, so the statement will execute.

Short Circuit Evaluation

When the compiler is evaluating an AND statement such as

while ( (x == 5) && (y == 5) )

the compiler evaluates the truth of the first statement (x==5); if this fails (that is, if x is not equal to five), the compiler does not go on to evaluate the truth or falsity of the second statement (y == 5) because AND requires that both be true.

Similarly, if the compiler is evaluating an OR statement such as

while ( (x == 5) || (y == 5) )

if the first statement is true (x == 5), the compiler never evaluates the second statement (y == 5) because the truth of either is sufficient in an OR statement.

Relational Precedence

Relational operators and logical operators, because they are C++ expressions, each return a value of true or false. Like all expressions, they have a precedence order (see Appendix B, "Operator Precedence") that determines which relations are evaluated first. This fact is important when determining the value of the statement

if ( x > 5 &&  y > 5  || z > 5)

It might be that the programmer wanted this expression to evaluate true if both x and y are greater than 5 or if z is greater than 5. On the other hand, the programmer might have wanted this expression to evaluate true only if x is greater than 5, and if it is also true that either y is greater than 5 or z is greater than 5.

If x is 3 and y and z are both 10, the first interpretation is true (z is greater than 5, so ignore x and y), but the second is false (it isn't true that x is greater than 5, and it therefore doesn't matter what is on the right side of the && symbol because both sides must be true.)

Although precedence determines which relation is evaluated first, parentheses can both change the order and make the statement clearer:

if (  (x > 5)  && (y > 5 ||  z > 5) )

Using the values that were mentioned earlier, this statement is false. Because it is not true that x is greater than 5, the left side of the AND statement fails, so the entire statement is false. Remember that an AND statement requires that both sides be true: Something isn't both "good tasting" AND "good for you" if it isn't good tasting.


NOTE: It is often a good idea to use extra parentheses to clarify what you want to group. Remember, the goal is to write programs that work and that are easy to read and understand. It is easier to understand

(8 * 5) + 3

than

8 * 5 + 3

even though the result is the same.


Putting It All Together

Following is the while statement you'll use to see whether you have a reasonable number of letters:

while ( howManyLetters < minLetters || howManyLetters > maxLetters )
{
     //...
}

This reads "As long as the condition is true, do the work between the braces." The condition that is tested is that either howManyLetters is less than minLetters OR howManyLetters is greater than maxLetters.

Thus, if the user enters 0 or 1, howManyLetters is less than minLetters, the condition is true, and the body of the while loop executes.

do while

Because howManyLetters is initialized to zero, you know that this while loop will run at least once. If you do not want to rely on the initial value of howManyLetters but you want to ensure that the loop runs at least once in any case, you can use a slight variant on the while loop--the do while loop:

do statement
    while ( condition )

This says that you will do the body of the loop while the condition is true. The loop must run at least once because the condition is not tested until after the statement executes the first time. So you can rewrite your loop as follows:

do
{
     //...
} while ( howManyLetters < minLetters || howManyLetters > maxLetters )

You know you need a do while loop when you are staring at a while loop and find your self saying, "Dang, I want this to run at least once!"


do while--A while loop that executes at least once and continues to exit while the condition that is tested is true.


Enumerated Constants

When I have constants that belong together, I can create enumerated constants. An enumerated constant is not quite a type; it is more of a collection of related constants.

The syntax for enumerated constants is to write the keyword enum, followed by the enumeration name, an open brace, each of the legal values (separated by commas), and a closing brace and a semicolon. Here's an example:

enum COLOR { RED, BLUE, GREEN, WHITE, BLACK };

This statement performs two tasks:

1. It makes COLOR the name of an enumeration.

2. It makes RED a symbolic constant with the value 0, BLUE a symbolic constant with the value 1, GREEN a symbolic constant with the value 2, and so on.

Every enumerated constant has an integer value. If you don't specify otherwise, the first constant has the value 0 and the rest count up from there. Any one of the constants can be initialized with a particular value, however, and those that are not initialized count upward from the ones before them. Thus, if you write

enum Color { RED=100, BLUE, GREEN=500, WHITE, BLACK=700 };

RED has the value 100; BLUE, the value 101; GREEN, the value 500; WHITE, the value 501; and BLACK, the value 700.

In this case you'll create an enum called BoundedValues and establish the values you need:

enum BoundedValues  
{ 
minPos = 2, 
maxPos = 10, 
minLetters = 2, 
maxLetters = 26 
};

This replaces the four constant integers described previously. Frankly, there often is little advantage to enumerated constants, except that they keep these values together in one place. If, on the other hand, you are creating a number of constants and you don't particularly care what their value is so long as they all have unique values, enumerated constants can be quite useful.

Enumerated constants are most often used for comparison or testing, which is how you use them here. You'll test whether minLetters is greater or less than these enumerated values.

Returning to the Code

Let's look at the code beginning on line 37 and ending on line 49:

37:        while ( howManyLetters < minLetters 
38:           || howManyLetters > maxLetters )
39:        {
40:           cout << "How many letters? (";
41:           cout << minLetters << "-" << maxLetters << "): ";
42:           cin >> howManyLetters;
43:           if ( howManyLetters < minLetters 
44:              || howManyLetters > maxLetters )
45:           {
46:              cout << "please enter a number between "; 
47:              cout << minLetters << " and " << maxLetters << endl;
48:           }
49:        }

The goal of this statement is to continue to prompt the user for an entry that is greater than minLetters and smaller than maxLetters.

The purpose of the if statement is to issue a reminder message if the number that is entered is out of bounds. Here's how the code reads in words:

37:        while ( howManyLetters < minLetters
38:           || howManyLetters > maxLetters )

While it is either true that the value howManyLetters is smaller than minLetters or it is true that howManyLetters is greater than maxLetters,

    cout << "How many letters? (";
    cout << minLetters << "-" << maxLetters << "): ";
    cin >> howManyLetters;

prompts the user and captures the user's response in the variable howManyLetters:

    if ( howManyLetters < minLetters 
    || howManyLetters > maxLetters )

Test the response; if it is either smaller than minLetters or greater than maxLetters,

{
     cout << "please enter a number between "; 
     cout << minLetters << " and " << maxLetters << endl;
}

prints out the reminder message.

The logic of this next while loop, shown on line 51, is identical to the preceding one.

Getting a Boolean Answer from the User

It is now time to ask the user whether he or she wants to allow duplicates (on line 72). You have a problem, however. The local variable duplicatesAllowed is of type bool, which, you'll remember, is a type that evaluates either to true or false.

You cannot capture a Boolean value from the user. The user can enter a number (using cin to save it in an int variable) or a character (using cin to save it in a character variable). There are some other choices as well, but Boolean is not one of them.

Here's how you'll do it: You'll prompt the user to enter a letter, y or n, and you'll then set the Boolean value based on what is entered.

The first task is to capture the response, and here you need something very much like the while logic that was shown previously for the letters. That is, you create a variable, as shown on line 65, initialize it to an invalid answer (in this case, space), and then continue to prompt until the user gives you an acceptable answer ('y' or 'n'):

char choice = ' ';
while ( choice != 'y' && choice != 'n' )
{
    cout << "Allow duplicates (y/n)? ";
    cin >> choice;
}

Begin by defining and initializing a character variable, choice. You can initialize it to a space by enclosing a space in single quotes, as described in Chapter 2.

Once again, you use a while loop to test whether you have valid data. This time, you will test to see whether choice is not equal to 'y' or 'n'.

If it is true that choice is not equal to (!=) 'y', and it is also true that choice is not equal to 'n', the expression returns true and the while statement executes.

Your next task is to test the value in choice (which must now be 'y' or 'n') and set duplicatesAllowed accordingly. You can certainly use an if statement:

if ( choice == 'y')
    duplicatesAllowed = true;
else
    duplicatesAllowed = false;

Equality Operator ==

The equality operator (==) tests whether two objects are the same. With integers, two variables are equal if they have the same value (for example, if x is assigned the value 4 and y is assigned the value 2*2, they are equal). With character variables, they are equal if they have the same character value. No surprises here. We test for equality on line 72 to see if choice is equal to the letter 'y'.


Equality operator (==)--Determines whether two objects have the same value. Be careful with this; you need two equal signs. A single equal sign (=) indicates assignment in C++. Thus, if you write

a = b

in C++ you assign the value currently in b to the variable a. If you want to test if they are equal, you must write

a == b


else

Often your program wants to take one branch if your condition is true, another if it is false. The keyword else indicates what the compiler is to execute if the tested expression evaluates false:

if (expression)
    statement;
else
    statement;

else--An else statement is executed only when an if statement evaluates to false.


Thus, the code shown says, "If choice is equal to y, set duplicatesAllowed to true; otherwise (else), set it to false."

The Conditional (or ternary) Operator

You're trying to assign the value duplicatesAllowed depending on the value of choice. In English you might want to say, "Is choice equal to y? If so, set duplicatesAllowed equal to true; otherwise, set it to false."

C++ has an operator that does exactly what you want.

The conditional operator (?:) is C++'s only ternary operator: It is the only operator to take three terms.


NOTE: The arity of an operator describes how many terms are used. For example, a binary operator, such as the addition operator (+), uses two terms: a+b. In this case, a and b are the two terms.

C++ has a few unary operators, but you've not seen them yet. The conditional operator is C++'s only ternary operator, and thus the terms conditional operator and ternary operator are often used interchangeably.



arity--How many terms an operator uses

unary--An operator that uses only one term

binary--An operator that uses two terms

ternary--An operator that uses three terms


The conditional operator takes three terms and returns a value. In fact, all three terms are expressions; that is, they can be statements that return a value:

(expression1) ? (expression2) : (expression3)

This line is read as follows: "If expression1 is true, return the value of expression2; otherwise, return the value of expression3." Typically, this value is assigned to a variable.

Thus, line 72 shows

duplicatesAllowed = (choice == y) ? true : false;

Figure 3.1 illustrates each of the operators and terms.

Figure 3.1 Dissecting a statement.

This line is read as follows: "Is it true that choice equals the character 'y'? If so, assign true to duplicatesAllowed; otherwise, assign false."

After you are comfortable with the conditional operator, it is clean, quick, and easy to use.

Putting It All Together

You are now ready to analyze the while loop, beginning at line 35. You start at line 27 by establishing valid as a Boolean operator that is initialized to false.

while ( ! valid )

The while loop executes while valid is false. Because valid was initialized to false, the while loop will certainly execute the first time through. This while loop begins with the opening brace on line 36 and ends at the closing brace on line 86.

This entire loop continues to execute until and unless valid is set to true.

Within this while loop are a series of interior while loops that solicit and test the values for howManyLetters, howManyPositions, and, ultimately (if indirectly), duplicatesAllowed:

 ( ! duplicatesAllowed && howManyPositions > howManyLetters )

Finally, after the values are gathered, on line 74 you test the logic of the choices. If it is true that duplicates are not allowed, and it is also true that howManyPositions (provided by the user) is greater than the number the user chose for howManyLetters, you have a problem: You need to put five letters in six positions without duplicating any letters--it can't be done.

In this case, you execute the if statement and write to the screen, "I can't put five letters in six positions without duplicates! Please try again." You then reinitialize howManyLetters and howManyPositions to zero. Make sure that you understand why. Hint: check the while loops in which these variables are assigned the user's choice.

If, on the other hand, the if statement fails (if duplicates are allowed or if howManyPositions is not greater than howManyLetters), the else statement executes, valid is set to true, and the while loop terminates.


Contents

© Copyright 1999, Macmillan Computer Publishing. All rights reserved.