Despite advances in the understanding of quantum mechanics, researchers at the world's leading institutes for science have finally boiled down the essence of matter into one of two states. Read on to find out about their amazing discovery!
Booleans are a conceptual thing and a technical thing in C++. Named after their famous discover, George Boole, Booleans (and their C++ type bool
) are either true (true
) or false (false
). Booleans remind me of the phrase Yoda made famous: "Do or do not. There is no try." Booleans are similar: True or False. There is no maybe.
Like all types in C++, bool
s have two characteristics:
- A range of valid values; and
- Valid operations on those values.
Like int
s which can only hold whole numbers (positive and negative), bool
s are limited in what they can hold. Valid values for bool
s are true
or false
. That's it.
What is cool about George Boole's thinking was that he defined a set of operations that you can use to manipulate Booleans and get another one (we'll come back to those operations in a second). What's even more awesome is that those operations closely reflect how logic works in real life!
Note: Remember that in C++ we use operators to specify an operation to perform.
So far we have learned that variables with the type bool
can only hold the values true
and false
. We also know that we have operations that combine two Booleans and get another. But, how do we get a Boolean value in the first place?
The answer: relational and equality operators! An expression composed of operands and a relational or equality operator has a value (just like all expressions!). And, like every value in C++, it has a type! The value can be either true
or false
and the type is bool
. I am sure that you aren't surprised!
Let's look at the relational and equality operators in "C++ World" and how they compare to the ways that we write comparisons when we are in "Math World."
Meaning | Math World | C++ World |
---|---|---|
Less than | < |
|
Less than or equal to | <= |
|
Greater than | > |
|
Greater than or equal to | >= |
|
Equal to | == |
|
Not equal to | != |
The first four are the relational operators and the final two are the equality operators.
Let's look at a few examples to make it clear:
#include <iostream>
int main() {
bool result{5 < 10};
return 0;
}
result
is true
!
#include <iostream>
int main() {
bool result{10 <= 10};
return 0;
}
result
is true
!
#include <iostream>
int main() {
bool result{10 >= 10};
return 0;
}
result
is true
!
int main() {
bool result{10 == 10};
return 0;
}
result
is true
!
int main() {
bool result{10 != 10};
return 0;
}
result
is false
!
Now, there's something really philosophical about bool
in C++. Like C++ is able to coerce an int
into a double
(or vice versa), C++ can coerce values that are not bool
into a bool
. That's really powerful (as we'll see later!). Although the specifics are sometimes odd, the basic idea is that if you assign a value (from a type such as an int
or a double
or char
) to a bool
and that value is 0
(0.0
or '\0'
), then the bool
is false
. For any other value, the bool
is true
. For instance,
#include <iostream>
int main() {
int zero{0};
double zero_point_zero{0.0};
char null_character{'\0'};
char letter_a{'a'};
int five_hundred_one{501};
double pi{3.14};
bool zero_bool{zero};
bool zero_point_zero_bool{zero_point_zero};
bool null_character_bool{null_character};
bool letter_a_bool{letter_a};
bool five_hundred_one_bool{five_hundred_one};
bool pi_bool{pi};
return 0;
}
zero_bool
, zero_point_zero_bool
, and null_character_bool
are all false
! The others are true
.
Now, let's get back to how we can combine Boolean values and get new Boolean values. Like +
, -
, *
, /
, etc. are primitive operations for numbers (int
egers and double
s), there are a few primitive operators for bool
s: &&
(which means and), ||
(which means or) and !
(which means not).
The first two (&&
and ||
) are binary operators (which mean that they take two operands, like +
, and -
do). The !
is a unary operator, meaning that it only takes a single operand.
Think about statements like:
It is raining and it is snowing.
and
It is raining or it is snowing.
Assume that we are in Cincinnati in June and I look outside. I see that it is raining, but there's no way that it's snowing. So, yes, it is true that it is raining (we'll call that raining
and say it is true
) but, no, it is not snowing (we'll call that snowing
and say that it is false
).
The first statement is logically not true! So, we would say that it is false
. The second statement, however, is true
. So, in
int main() {
bool raining{true};
bool snowing{false};
bool is_raining_and_snowing{raining && snowing};
bool is_raining_or_snowing{raining || snowing};
}
is_raining_and_snowing
is false
and is_raining_or_snowing
is true
.
In an ||
expression, if either operand is true
, then the value of the expression is true
. In an ||
expression, if both of the operands are false
, then the value of the expression is false
. In an &&
expression, if both operands are true
, then the expression is true
. Otherwise, the expressions are false
.
The !
simply reverses the value of its (single) operand.
There is a cool chart that we can write that describes completely the values for combinations of Boolean values using the and, or and not operators. The chart is called a truth table. Here is the truth table for the and operator:
a | b | a && b |
---|---|---|
true |
true |
true |
true |
false |
false |
false |
true |
false |
false |
false |
false |
Notice that the only combination that is true
is when both a and b are true
. Interesting!
Now, here is the truth table for the or operator:
a | b | a || b |
---|---|---|
true |
true |
true |
true |
false |
true |
false |
true |
true |
false |
false |
false |
Notice that the only combination that is true
is when one of a and b are true
. Doubly interesting!
The not operator is different than the and and the or operators. The not operator needs only one operand (a so-called unary operator). Why is that? Well, the not operator simply reverses the value of a Boolean. So, it would make sense that it would only work on one operator. Here's the truth table for the not operator:
a | ! a |
---|---|
true |
false |
false |
true |
Let's play computer and try to figure out the value of no
in the following C++ code:
#include <iostream>
int main() {
bool yes{true};
bool no{!yes};
return 0;
}
no
is false
. To compute that, just replace yes
with its value (bool no{!true}
), flip true
to false
because of the !
and you get bool no{false};
. Pretty cool!
So, we can calculate true
and false
values, but what good are they? Read on to find out!
To date, the programs that we have written in class and in lab have been pretty straightforward, as in their execution proceeds straight from one statement to the next. They may have calculated different values each time they executed based on user input, but they performed the same statements no matter what. In other words, until now our programs have operated sequentially. With an if
statement we can get our programs to operate selectively. In other words, depending on a runtime calculation, our program can execute different statements! Although conceptually very simple, the power of this selective exectution is infinite! The general form of an if
statement is
if (expression) {
some statements;
}
When boolean expression is true
, then some statements
execute. When boolean expression is false
, some statements do not execute. some statements between {}
are called a block. You can nest blocks of code inside one another without limit (some caveats apply to that statement)! C++ programmers say that a block of code is "conditionally executed" by an if
statement. C++ programmers also often say that a block of code is "guarded by a conditional".
#include <iostream>
int main() {
int five{5};
int six{6};
if (5 < 6) {
std::cout << "Five is less than 6!\n";
}
if (five == six) {
std::cout << "Huh, five is equal to 6!\n";
}
return 0;
}
prints
Five is less than 6!
Because 5
is less than 6
, the std::cout
statement inside the block associated with the 5 < 6
condition executes and Five is less than six!
is printed to the screen. However, the value of five
does not equal the value of six
so the std::cout
inside the block associated with the five == six
condition does not execute.
C++ tries to be very accommodating and will attempt to convert a value of any type to a bool
when it is used in the place of boolean expression in an if
statement. Remember, from earlier, how we talked about the ways that conversions happen?
More importantly, remember how we talked about the fact that many things in C++ are expressions that you would not immediately realize?
Well, one of those hidden expressions in C++ is the =
(assignment) operator! Yes, an assignment statement like a = b
is, in fact, an expression and its value is the value of b
! In other words,
#include <iostream>
int main() {
int a{5};
int b{6};
std::cout << "a = b: " << (a = b) << "\n";
return 0;
}
prints
a = b: 6
I know, right?! These semantics make it possible to do things like
a = b = c = d = 5;
to set a
, b
, c
and d
all to the value 5
.
Now, think closely and figure out why
#include <iostream>
int main() {
int five{5};
int six{6};
if (five = six) {
std::cout << "Huh, five is equal to 6!\n";
}
return 0;
}
prints
Huh, five is equal to 6!
If you look closely, you will see that where I put boolean expression in the if
statement there is a =
and not a ==
. The expression evaluates to the value in six
. Remember earlier how we learned that C++ interprets anything that is not a bool
and is not another type whose value is something like 0
(etc.) as true? Well, in the case shown above, C++ evaluates whether the value of the expression five = six
is 0 (it's not, it's the value in six
), determines that the value equals true
and chooses to execute the block!
Be very careful! You will make this mistake! I promise!