Flow control
Concepts
Section titled “Concepts”ifelseelse ifwhile (true)continuebreakdo ... while
Understanding examples
Section titled “Understanding examples”1. true in a condition
Section titled “1. true in a condition”if (true){ std::cout << "Hello" << std::endl;}Answer
true in a condition causes the body of the if statement to always execute.
2. false in a condition
Section titled “2. false in a condition”if (false){ std::cout << "Hello" << std::endl;}Answer
The body of the if statement is guaranteed not to execute.
3. A bool variable in a condition
Section titled “3. A bool variable in a condition”bool execute = true;if (execute){ std::cout << "Hello" << std::endl;}
bool notExecute = !execute;if (notExecute){ std::cout << "Not executed" << std::endl;}Answer
When run, only Hello will be printed.
4. Numbers in a condition
Section titled “4. Numbers in a condition”if (1){ std::cout << "1" << std::endl;}if (123){ std::cout << "123" << std::endl;}if (0){ std::cout << "0" << std::endl;}Answer
In C++, if accepts any expression that can be converted to bool.
This code will compile and print 1 and 123.
5. A pointer in a condition
Section titled “5. A pointer in a condition”int* a { nullptr };if (a){ std::cout << "a is true" << std::endl;}
int b {};int* pb { &b };if (pb){ std::cout << "pb is true" << std::endl;}if (b){ std::cout << "b is true" << std::endl;}
bool c { false };bool* pc { &c };if (pc){ std::cout << "pc is true" << std::endl;}Answer
When the condition is a pointer, if checks whether it is non-null (not nullptr).
a is true will not be printed here, while pb is true and pc is true will be printed.
if (b) is equivalent to if (0), which is equivalent to if (false).
b is true will not be printed.
6. Multiple statements in the body
Section titled “6. Multiple statements in the body”if (false){ std::cout << "A" << std::endl; std::cout << "B" << std::endl;}Answer
Nothing will be printed.
7. Without braces
Section titled “7. Without braces”if (false) std::cout << "A" << std::endl; std::cout << "B" << std::endl;Answer
Only B will be printed because only the first statement is attached to the if.
Equivalent code:
if (false){ std::cout << "A" << std::endl;}std::cout << "B" << std::endl;8. else
Section titled “8. else”if (false){ std::cout << "A" << std::endl;}else{ std::cout << "B" << std::endl;}Answer
This code will print B, since the else block executes when the condition is not satisfied.
9. Reassigning a condition variable inside the if body
Section titled “9. Reassigning a condition variable inside the if body”bool a = true;if (a){ a = false;}else{ std::cout << "B" << std::endl;}Answer
B will not be printed because whether else executes is determined when a is checked in the if,
which happens before it is changed.
10. return in an if
Section titled “10. return in an if”void F(){ if (true) { return; } else { std::cout << "Else" << std::endl; } std::cout << "After Else" << std::endl;}Answer
Else will not be printed here.
Whether the else block executes depends only on the condition in the if.
return exits the function immediately, so After Else will not be printed either.
11. Code after else
Section titled “11. Code after else”if (true) std::cout << "A" << std::endl;else std::cout << "B" << std::endl;std::cout << "C" << std::endl;Answer
B will not be printed. A and C will be printed.
12. if without braces, else with braces
Section titled “12. if without braces, else with braces”if (true) std::cout << "A" << std::endl;else{ std::cout << "B" << std::endl;}Answer
It is valid to use an unbraced statement after if and an explicit block after else.
13. An if-else chain
Section titled “13. An if-else chain”How would this code usually be written using an if-else chain?
if (a){ std::cout << "A" << std::endl;}else{ if (b) { std::cout << "B" << std::endl; } else { if (c) { std::cout << "C" << std::endl; } }}Answer
An if statement is usually placed directly after else:
if (a){ std::cout << "A" << std::endl;}else if (b){ std::cout << "B" << std::endl;}else if (c){ std::cout << "C" << std::endl;}14. Early return
Section titled “14. Early return”Try to represent this code as an equivalent if-else chain.
How can this code be written using an early return / guard clause?
if (a){ std::cout << "A" << std::endl;}else{ std::cout << "After A" << std::endl;
if (b) { std::cout << "B" << std::endl; } else { std::cout << "After B" << std::endl;
if (c) { std::cout << "C" << std::endl; } else { std::cout << "After C" << std::endl; } }}Answer (chain)
This code cannot be represented as an if-else chain without duplicating code.
There is nowhere to put After B and After C so that they execute according to the same rules without duplicating them.
You could duplicate them, as shown below, but that would make the code harder to maintain:
if (a){ std::cout << "A" << std::endl;}else if (b){ std::cout << "After A" << std::endl; std::cout << "B" << std::endl;}else if (c){ std::cout << "After A" << std::endl; std::cout << "After B" << std::endl; std::cout << "C" << std::endl;}else{ std::cout << "After A" << std::endl; std::cout << "After B" << std::endl; std::cout << "After C" << std::endl;}Answer (guard clause / early return)
- Put this code in a function.
- Add a
returnstatement to eachifblock. - Then the
elsestatements and blocks can be removed.
#include <iostream>
void f(bool a, bool b, bool c){ if (a) { std::cout << "A" << std::endl; return; } std::cout << "After A" << std::endl;
if (b) { std::cout << "B" << std::endl; return; } std::cout << "After B" << std::endl;
if (c) { std::cout << "C" << std::endl; return; } std::cout << "After C" << std::endl;}
int main(){ // предполагается, что a, b, c были созданы ранее ... f(a, b, c);}Why use this guard clause / early return?
- To move error handling to the top of the function and the main logic below it. This makes it clear that the main logic depends on the conditions checked during error handling (the contract).
- It removes unnecessary nesting of conditions;
- It promotes locality between error checks and their handling.
Example code without using a guard clause / early return:
void sendWelcomeEmail(User* user){ // Условия перечислены с увеличением вложенности. if (user != nullptr) { if (user->IsActive) { if (user->EmailConfirmed) { // Код с самим действием спрятан в середине функции. std::cout << "Sending email to " << user->email << std::endl; } else { std::cout << "Email not confirmed." << std::endl; } } else { std::cout << "User is not active." << std::endl; } } // Не соблюдается локальность: // обработка удалена в исходном коде от связанной проверки. else { std::cout << "User not found." << std::endl; }}The same code using a guard clause / early return:
void sendWelcomeEmail(User* user){ // Можно блоком разграничить контракт // (необходимые условия для выполнения основного действия), // или вынести его в свою функцию. { // Соблюдена локальность: условия рядом с их обработкой. if (user == nullptr) { std::cout << "User not found." << std::endl; return; }
if (!user->isActive) { std::cout << "User is not active." << std::endl; return; }
if (!user->emailConfirmed) { std::cout << "Email not confirmed." << std::endl; return; } }
// Основной код находится после всех проверок, а не в середине. std::cout << "Sending email to " << user->email << std::endl;}15. A complex example
Section titled “15. A complex example”int i = 0;while (true){ if (i == 4) { std::cout << "ERROR: Should not happen" << std::endl; break; } if (i == 3) { std::cout << "Exit" << std::endl; break; } if (i == 0) { std::cout << "Increase by 2 on first iter" << std::endl; i += 2; continue; }
std::cout << "Increase by 1 normally" << std::endl; i++;}What do break and continue do?
break stops the loop; execution continues with the first statement after it.
continue starts the next iteration of the loop (the remaining statements in the loop body are not executed for that iteration).
Answer
“Increase by 2 on first iter” will be printed in the first loop iteration; i++ will not execute because continue skips it.
“Increase by 1 normally” will be printed in the second loop iteration,
after all if checks have failed.
“Exit” will be printed in the third iteration, while the i == 0 check and the i++ statement will not execute,
because break stops the loop.
Increase by 2 on first iterIncrease by 1 normallyExit16. return from inside while
Section titled “16. return from inside while”int F(){ while (true) { if (true) { return 0; } break; } return 1;}Answer
The function will return 0. break and return 1 will not execute.
17. An example of using if
Section titled “17. An example of using if”#include <iostream>#include <cassert>
struct Result{ bool isDenominatorZero; // Should only be read if there is no error. int result;};
Result ceilingDivide(int numerator, int denominator){ if (denominator == 0) { return { .isDenominatorZero = true, .result = 0, }; }
int value = (numerator + denominator - 1) / denominator; return { .isDenominatorZero = false, .result = value, };}
int main(){ { int totalStudents { 25 }; int studentsPerTable { 2 }; Result result { ceilingDivide(totalStudents, studentsPerTable) }; if (!result.isDenominatorZero) { int numTablesNeeded = result.result; std::cout << "Number of tables needed is " << numTablesNeeded << std::endl; } else { std::cout << "Can't seat any students if no students are seated per table" << std::endl; assert(studentsPerTable == 0); } } { Result result { ceilingDivide(30, 0) }; if (result.isDenominatorZero) { std::cout << "Could not divide" << std::endl; } else { std::cout << "The result is " << result.result << std::endl; } }}Refactoring
Section titled “Refactoring”Refactor this code using a guard clause / early return.
#include <iostream>
int main(){ int number = 10;
if (number == 5) { std::cout << "The number is 5" << std::endl; } else { std::cout << "The number is not 5" << std::endl;
if (number % 2 == 1) { std::cout << "The number is not divisible by 2" << std::endl; } else { std::cout << "The number is divisible by 2" << std::endl;
if (number == 6) { std::cout << "The number is 6" << std::endl; } } }}Complex logical expression
Section titled “Complex logical expression”Here is the starter code:
#include <iostream>
int main(){ int apples; int pears; int oranges;
std::cout << "Apples:"; std::cin >> apples;
std::cout << "Pears:"; std::cin >> pears;
std::cout << "Oranges:"; std::cin >> oranges;
// ...
return 0;}-
If the number of apples is greater than 5, the number of pears is less than 8, and the number of oranges is exactly 2 times the number of apples, print “Hello”.
-
Create a temporary variable containing the result of the expression. Check it in an
ifstatement. -
Create a temporary variable for each subcondition. Calculate the overall condition using these subconditions.
-
Declare the temporary subcondition variables in a block so that they are not visible outside the block. Use the outer condition outside the block (the block exists to initialize the overall condition).
You may also turn this block into a function.
-
Refactor the 3 variables holding fruit quantities into a single structure (
FruitCounts) with 3 fields, one for each fruit type. Rewrite the code so this structure is used everywhere.