Chapter 7
Deciding Things: if, else, and Booleans
Comparison, and, or, and chains that stay readable.
Every program you’ve written runs every line, top to bottom, whatever happens. It can’t skip anything and it can’t choose.
That changes here. And three earlier chapters have been waiting for this one:
chapter 3’s bool finally does something, chapter 4’s npos becomes usable, and
chapter 6’s warning about comparing decimals comes due.
Asking a question
int age = 20;
if (age >= 18) {
std::cout << "You can vote.\n";
}
The thing in the brackets is a condition: something that is either true or false. If it’s true, the code in the braces runs. If it isn’t, the whole block is skipped as though you never wrote it.
There are six ways to compare:
| Operator | Asks |
|---|---|
== | are these the same? |
!= | are these different? |
< | is the left one smaller? |
> | is the left one bigger? |
<= | smaller or the same? |
>= | bigger or the same? |
And here’s what chapter 3’s odd little type was for. A comparison produces a
bool:
bool can_vote = (age >= 18);
std::cout << can_vote << '\n';
1
Still printing 1 rather than true, as chapter 3 warned. But now you can put
that bool straight into an if, which is what it was always for.
The mistake everyone makes once
Chapter 3 said = and == are different operators. Here’s what happens when you
reach for the wrong one:
int age = 20;
if (age = 0) {
std::cout << "this ran\n";
} else {
std::cout << "this ran instead\n";
}
std::cout << "age is now " << age << '\n';
this ran instead
age is now 20
Except that isn’t what it prints. It prints:
this ran instead
age is now 0
Read the condition again. age = 0 doesn’t ask whether age is zero. It sets
age to zero, and then hands the result to the if. Your variable is destroyed as a
side effect of asking a question you never actually asked.
Otherwise
else catches everything the if didn’t:
if (age >= 18) {
std::cout << "You can vote.\n";
} else {
std::cout << "Not yet.\n";
}
And else if chains several questions together, checked in order until one fits:
if (age < 5) {
std::cout << "Free\n";
} else if (age < 18) {
std::cout << "Child\n";
} else if (age < 65) {
std::cout << "Adult\n";
} else {
std::cout << "Senior\n";
}
Order matters enormously. C++ takes the first branch that’s true and skips the
rest, so a 3-year-old never reaches the age < 18 test, even though that’s also
true of them. Write the narrowest cases first, or the broad ones swallow
everything.
Combining questions
Three operators let you ask about more than one thing:
&&is and. True only if both sides are true.||is or. True if either side is.!is not. It flips true and false.
if (age >= 18 && member) {
std::cout << "Adult member\n";
}
if (age < 5 || age >= 65) {
std::cout << "Reduced price\n";
}
Read them out loud, “age is at least 18 and member”, and they’re exactly as
obvious as they look. Use brackets when you mix && and ||, for the same reason
you used them for arithmetic: nobody wants to look up precedence rules.
Two comparisons that surprise people
Text compares the way you’d hope.
std::string a = "ada";
std::string b = "ada";
std::cout << (a == b) << '\n';
1
== on two std::strings compares the actual characters. That sounds unremarkable
until chapter 24, where you’ll find that the older kind of C++ text does something
completely different and far worse. This is std::string being kind to you.
< and > work too, comparing alphabetically, with a catch. "Ada" < "ada" is
true, because capital letters come before lowercase ones in the character codes.
Sorting names case-insensitively is a job you have to do deliberately.
Decimals do not compare the way you’d hope.
double x = 0.1 + 0.2;
std::cout << (x == 0.3) << '\n';
0
False. Chapter 6 showed why: 0.1 + 0.2 is really 0.30000000000000004, and
that is not 0.3. Never compare two doubles with ==. Ask whether they’re
close enough instead: the difference between them, ignoring the sign, being
smaller than some tiny amount you choose.
Finding out whether something was found
Chapter 4 left you with a cliffhanger: find returns an enormous number when it
fails, called std::string::npos, and you had no way to act on it. Now you do:
std::string full = "Ada Lovelace";
auto space = full.find(' ');
if (space == std::string::npos) {
std::cout << "No space in that name.\n";
} else {
std::cout << "Space at position " << space << '\n';
}
That’s the pattern you’ll use every time you search for anything: search, then check whether it was found, and only then use the position.
Exercise 1 · Break your own ticket rules
Write the age chain from earlier, but put the branches in the wrong order, then test
age < 65 before age < 18.
Run it with an age of 12 and watch a child get charged as an adult. No error, no warning; the program is doing exactly what you told it. Then put the order back and notice that you now understand why it has to be that way round.
Check yourself
Project
Ticket pricing with real rules
Roughly 45 minutes
A cinema charges by age, with a weekend surcharge. Ask for the details and print the price.
The rules:
- under 5: free
- 5 to 17: £6
- 18 to 64: £10
- 65 and over: £7
- weekends add £2, but a free ticket stays free
Age? 12
Weekend? (y/n) y
Price: 8Read the weekend answer into a char with std::cin >> weekend; and compare it
with 'y' in single quotes: one character, exactly as chapter 4 described.
Check yours against these:
| Age | Weekend | Price |
|---|---|---|
| 3 | n | 0 |
| 3 | y | 0 |
| 12 | y | 8 |
| 30 | n | 10 |
| 70 | y | 9 |
The third and fifth rows are the ones that catch people. The surcharge is a
separate decision from the age band, and it needs && to avoid charging a
toddler £2 for the privilege of being free.
Stretch: add a members’ discount of 10% off the final price, and decide for yourself whether it applies before or after the weekend surcharge. There’s no right answer, but there is a right way to find out, which is to write both and compare the numbers.