Control flow
8.1 — Control Flow Introduction¶
By default, programs execute sequentially (top to bottom). Control flow statements break this linearity:
-
Conditional branches: Execute code based on a condition (
if,switch). -
Loops: Repeat code multiple times (
while,do-while,for). -
Humps/Halts: Jump to another part of the program or stop it entirely (
goto,break,continue,exit).
8.2 & 8.3 — If Statements: Basics and Pitfalls¶
-
Implicit Blocks: If you don't use curly braces
{ }, only the single next statement is considered part of theif.- Best Practice: Always use braces, even for one-liners, to avoid the "dangling else" problem or accidental logic errors.
-
Chaining: Use
else ifto create a mutually exclusive chain of conditions. -
Common Problems: * Using
=(assignment) instead of==(equality) inside the condition.- Null statements:
if (condition);(the semicolon ends the if-statement immediately, doing nothing).
- Null statements:
8.4 — Constexpr If Statements¶
Introduced in C++17, if constexpr allows for compile-time branching.
-
If the condition is false at compile-time, the compiler completely discards the code inside the block.
-
This is primarily used in template programming to handle different data types differently without causing compilation errors for types that don't support certain operations.
8.5 & 8.6 — Switch Statements¶
A switch is often more readable than a long chain of if-else statements when comparing one variable against multiple constant values.
-
Labels: Must be constant expressions (literals or
constexpr). -
The
defaultlabel: Executes if no match is found. -
Fallthrough: Execution continues into the next case unless a
breakis hit.- Use the
[[fallthrough]]attribute in C++17 to tell the compiler (and other programmers) that fallthrough is intentional.
- Use the
-
Scoping: You cannot define variables with initialization inside a case unless you wrap the case in a block
{ }.
8.7 — Goto Statements¶
goto allows an unconditional jump to a labeled statement.
-
Why it's "evil": It can create "spaghetti code" that is nearly impossible to follow.
-
The only exception: Occasionally used to break out of deeply nested loops, though modern C++ usually offers better alternatives.
8.8, 8.9 & 8.10 — Loops: While, Do-While, and For¶
-
While Loop: Checks the condition before executing the body. It may run zero times.
-
Do-While Loop: Executes the body first and then checks the condition. It is guaranteed to run at least once.
-
For Loop: Best for when you know how many times you need to iterate.
-
Structure:
for (init-statement; condition; iteration-expression) -
Off-by-one errors: The most common loop bug (e.g., using
<=when you should use<).
-
8.11 — Break and Continue¶
-
Break: Immediately exits the current loop or switch statement.
-
Continue: Skips the rest of the current loop iteration and jumps straight to the next condition check (or the iteration expression in a
forloop).
8.12 — Halts¶
Halts stop the program entirely.
-
std::exit(): Cleans up static objects and returns a status code to the OS. -
std::abort(): Terminates the program immediately without cleanup (usually for critical failures).
8.13, 8.14 & 8.15 — Random Number Generation (RNG)¶
Computers are deterministic, so they generate "pseudo-random" numbers based on an initial "seed."
-
The Old Way:
std::rand()is considered poor quality and should be avoided in modern C++. -
Mersenne Twister (
std::mt19937): The standard for high-quality pseudo-random numbers in C++ (found in the<random>header). -
The Process:
-
Seed: Use
std::random_deviceto get a non-deterministic seed. -
Engine: Initialize the Mersenne Twister with the seed.
-
Distribution: Use a distribution (like
std::uniform_int_distribution) to map the raw bits into a specific range (e.g., 1 to 6).
-
-
Global RNG: It is best practice to initialize your RNG engine once (globally or as a static variable) and reuse it, rather than re-seeding every time you need a number.