Decision Making
Decision making lets a program choose different execution paths based on conditions — the if, if-else, else-if, nested if, and switch constructs are the backbone of nearly every non-trivial C++ program.
Decision making allows a C++ program to choose different execution paths based on conditions. A program can evaluate a condition and decide whether to execute a block of code, skip a block, choose between multiple alternatives, select one option from several cases, or execute code only when a condition is satisfied. The primary decision-making constructs in C++ are if, if-else, else-if, nested if, and switch.
How Conditions Work in C++
A condition is an expression whose result is evaluated in a Boolean context.
If condition evaluates to true, the block runs; otherwise it is skipped.
Conceptual flow of a single condition check.
In C++, a condition can involve comparison operators, logical operators, Boolean values, variables, function calls, or other expressions.
age >= 18 produces a Boolean result, which is what the if statement evaluates.
if (condition)
{
// code
}Condition
|
+------+------+
| |
true false
| |
execute skip
|
continueint age = 20;
if (age >= 18)
{
std::cout << "Adult";
}if Statement
The if statement executes a block only when its condition is true.
General syntax of an if statement.
If the condition is true, the statement executes; if false, the block is skipped entirely.
if (condition)
{
// statements
}int temperature = 35;
if (temperature > 30)
{
std::cout << "It is hot";
}Boolean Conditions
C++ has a dedicated bool type, and a variable of that type can be used directly as a condition.
isReady itself is the condition — no comparison is needed.
A Boolean literal can also be written directly as the condition.
bool isReady = true;
if (isReady)
{
std::cout << "System is ready";
}if (true)
{
std::cout << "This executes";
}
if (false)
{
std::cout << "This does not execute";
}Conditions Using Comparisons
Comparison operators are commonly used in decision making.
speed > 60 evaluates to true, so the message prints.
Multiple comparisons can be used in separate conditions across a program.
Operator | Meaning
---------|----------------------------
== | Equal to
!= | Not equal to
> | Greater than
< | Less than
>= | Greater than or equal to
<= | Less than or equal toint speed = 80;
if (speed > 60)
{
std::cout << "Overspeed";
}if (speed >= 40)
{
std::cout << "Speed is acceptable";
}if-else
if-else provides two possible execution paths — one for when the condition is true, and one for when it is false.
General syntax of if-else.
number % 2 == 0 is false for 7, so the else branch runs and prints Odd.
Flow diagram of the if-else decision.
if (condition)
{
// executed when true
}
else
{
// executed when false
}int number = 7;
if (number % 2 == 0)
{
std::cout << "Even";
}
else
{
std::cout << "Odd";
}number % 2 == 0
|
+------+------+
| |
true false
| |
Even Oddelse-if Chain
When there are multiple possible conditions, use an else-if chain.
General syntax of an else-if chain.
Only the first matching branch is executed.
marks >= 90 -> false marks >= 80 -> false marks >= 70 -> true, so "Grade B" prints the remaining conditions are not evaluated after that matching branch
For marks = 75, execution stops at the first true condition (marks >= 70) and prints Grade B — later else-if branches are never checked.
if (condition1)
{
// ...
}
else if (condition2)
{
// ...
}
else if (condition3)
{
// ...
}
else
{
// none matched
}int marks = 75;
if (marks >= 90)
{
std::cout << "Grade A+";
}
else if (marks >= 80)
{
std::cout << "Grade A";
}
else if (marks >= 70)
{
std::cout << "Grade B";
}
else if (marks >= 60)
{
std::cout << "Grade C";
}
else
{
std::cout << "Fail";
}Multiple Conditions with Logical AND (&&)
Logical operators allow conditions to be combined. With logical AND (&&), both conditions must be true for the combined condition to be true.
Both age >= 18 and hasLicense must be true for the message to print.
Truth table for the && operator.
int age = 25;
bool hasLicense = true;
if (age >= 18 && hasLicense)
{
std::cout << "Allowed to drive";
}condition1 && condition2
true && true -> true
true && false -> false
false && true -> false
false && false -> falseLogical OR (||)
With logical OR (||), at least one condition must be true for the combined condition to be true.
Either day == 6 or day == 7 being true is enough for the message to print.
Truth table for the || operator.
int day = 7;
if (day == 6 || day == 7)
{
std::cout << "Weekend";
}true || true -> true
true || false -> true
false || true -> true
false || false -> falseLogical NOT (!)
The ! operator reverses a Boolean condition.
!isReady inverts the value of isReady, turning false into true.
Truth table for the ! operator.
bool isReady = false;
if (!isReady)
{
std::cout << "System is not ready";
}!true -> false
!false -> trueShort-Circuit Evaluation
C++ evaluates && and || with short-circuit behavior — the second operand is only evaluated when its result could still change the outcome.
With &&, if the left condition is false, the right condition does not need to be evaluated. If ptr == nullptr, *ptr == 10 is never evaluated, which prevents an invalid dereference.
With ||, if the left condition is true, the right condition does not need to be evaluated. If isConnected is true, reconnect() is never called.
if (ptr != nullptr && *ptr == 10)
{
// ...
}if (isConnected || reconnect())
{
std::cout << "Connection available";
}Nested if
An if statement can exist inside another if. The inner condition is checked only when the outer condition is true.
The inner if only runs when age >= 18 is true; hasLicense is checked only in that case.
Flow diagram of the nested condition check.
Nested conditions are useful when one decision depends on another, but excessive nesting can make code difficult to read.
int age = 25;
bool hasLicense = true;
if (age >= 18)
{
if (hasLicense)
{
std::cout << "Driving allowed";
}
}age >= 18?
/ \
no yes
| |
stop hasLicense?
/ \
no yes
| |
stop allowedif Without Braces
C++ permits a single statement without braces.
Only the single statement immediately after the if belongs to it.
However, braces are generally clearer, especially when code may later be modified.
Braces make the scope of the if body explicit even for a single statement.
If a second statement is added later, braces ensure both statements are part of the if body.
Using braces consistently reduces accidental control-flow errors.
if (x > 0)
std::cout << "Positive";if (x > 0)
{
std::cout << "Positive";
}if (x > 0)
{
std::cout << "Positive";
logValue(x);
}switch Statement
switch is useful when one expression needs to be compared against several constant case values.
General syntax of a switch statement.
option matches case 2, so the output is Stop.
switch (expression)
{
case value1:
// statements
break;
case value2:
// statements
break;
default:
// no matching case
break;
}int option = 2;
switch (option)
{
case 1:
std::cout << "Start";
break;
case 2:
std::cout << "Stop";
break;
case 3:
std::cout << "Reset";
break;
default:
std::cout << "Invalid option";
break;
}How switch Works
The switch expression is evaluated once, and control transfers directly to the matching case.
Conceptual flow of a switch statement.
expression
|
+---------+---------+
| | |
case 1 case 2 case 3
| | |
code code code
|
breakbreak in switch
break exits the switch, so only the matched case's statements run.
Each case ends with break, so execution leaves the switch immediately after the matching case runs.
Without break, execution can continue into subsequent cases.
switch (value)
{
case 1:
std::cout << "One";
break;
case 2:
std::cout << "Two";
break;
}Fallthrough
For value == 1, execution enters case 1, and because it has no break, it continues into case 2. Output: OneTwo. This behavior is called fallthrough.
Intentional fallthrough can be useful when several cases should share the same code.
Both 6 and 7 reach the same code because case 6 has no statements of its own before falling into case 7.
Modern C++ also provides the [[fallthrough]] attribute to explicitly document intentional fallthrough.
int value = 1;
switch (value)
{
case 1:
std::cout << "One";
case 2:
std::cout << "Two";
break;
}switch (day)
{
case 6:
case 7:
std::cout << "Weekend";
break;
default:
std::cout << "Weekday";
break;
}switch (value)
{
case 1:
prepare();
[[fallthrough]];
case 2:
execute();
break;
default:
break;
}default Case
default executes when none of the cases match.
If option is neither 1 nor 2, the default branch runs.
The default case is optional, but it is often useful for handling unexpected values.
switch (option)
{
case 1:
std::cout << "Start";
break;
case 2:
std::cout << "Stop";
break;
default:
std::cout << "Unknown option";
break;
}What Can Be Used in switch?
The controlling expression must have an appropriate integral, enumeration, or class type that can be converted to an integral or enumeration type for the switch selection.
An int is a common example of a valid switch expression type.
Characters can also be used as the switch expression and as case labels.
case labels must be compile-time constant expressions, and duplicate case values are not allowed.
int choice = 2;
switch (choice)
{
case 1:
break;
case 2:
break;
}char command = 'A';
switch (command)
{
case 'A':
std::cout << "Add";
break;
case 'D':
std::cout << "Delete";
break;
}if-else vs switch
A side-by-side comparison of when if-else or switch tends to fit better.
Use if-else for conditions such as ranges and complex expressions.
Use switch for fixed, discrete choices.
Feature | if-else | switch
-------------------------------|-----------|------------------------
Multiple conditions | Excellent | Limited
Ranges | Yes | Not directly
Complex expressions | Yes | Not directly in case labels
Equality against fixed values | Yes | Excellent
Boolean conditions | Yes | Not its primary purpose
Fallthrough | No | Yes
Readability for menu/options | Sometimes | Often betterif (temperature > 100)
{
// ...
}
else if (temperature > 50)
{
// ...
}switch (command)
{
case 1:
// ...
break;
case 2:
// ...
break;
}Nested switch
A switch can contain another switch, although excessive nesting should generally be avoided.
The inner switch on mode only runs once the outer switch has matched case 1 on system.
switch (system)
{
case 1:
switch (mode)
{
case 1:
std::cout << "System 1, Mode 1";
break;
case 2:
std::cout << "System 1, Mode 2";
break;
}
break;
default:
break;
}Conditional Operator
C++ also provides the conditional operator ?: for choosing between two expressions. Syntax: condition ? expression1 : expression2.
If age >= 18 is true, result becomes "Adult"; otherwise it becomes "Minor".
This is the equivalent decision logic written with if-else instead of the conditional operator.
The conditional operator is useful for simple value selection. For complicated logic, a normal if-else is usually easier to read.
int age = 20;
std::string result = (age >= 18) ? "Adult" : "Minor";if (age >= 18)
{
result = "Adult";
}
else
{
result = "Minor";
}Decision-Making Flow
A typical decision structure evaluates a condition and then follows one of two branches before continuing.
Flow of a single true/false decision.
Flow when there are multiple conditions to check in sequence, as in an else-if chain.
Start
|
Evaluate condition
|
+------+------+
| |
true false
| |
Execute A Execute B
| |
+------+------+
|
Continuecondition 1?
/ \
yes no
| |
execute 1 condition 2?
/ \
yes no
| |
execute 2 defaultImportant Difference: Condition vs Execution Order
A condition determines which branch is selected. It should not be confused with operator precedence or evaluation-order rules.
The if statement determines whether the body executes.
The && operator also has short-circuit behavior, so b < 20 is not evaluated if a > 10 is false.
if (a > 10 && b < 20)
{
// ...
}Best Practices
1. Use braces, even for a single-statement body.
2. Keep conditions readable. Prefer a clear combination such as age >= 18 && hasLicense over unnecessarily complicated expressions.
3. Order mutually exclusive conditions carefully — the more specific or higher threshold should be checked first.
4. Use switch for fixed alternatives, when one expression needs to be compared against several constant values.
5. Make intentional fallthrough obvious — use appropriate grouping or [[fallthrough]] when it is intentional.
6. Avoid deeply nested decision logic — break complicated logic into clearer conditions or functions when appropriate.
7. Prefer x == 10 over x = 10 inside a condition, and consider writing the constant on the left (10 == x) or enabling compiler warnings if accidental assignment inside a condition is a concern.
8. Write range checks as two explicit comparisons joined with &&, e.g. x > 0 && x < 10, rather than a chained form — C++ does not evaluate 0 < x < 10 as a mathematical range check.
9. For logical combination, use && and || (not the bitwise & and |), which also gives short-circuit evaluation.
if (condition)
{
doSomething();
}if (age >= 18 && hasLicense)
{
// ...
}if (score >= 90)
{
// A+
}
else if (score >= 80)
{
// A
}switch (command)
{
case 1:
// ...
break;
case 2:
// ...
break;
default:
// ...
break;
}Complete Example
A complete grading program that combines validity checking with an else-if chain to assign a grade.
Read marks from the user If marks is out of the 0-100 range, print Invalid marks
Otherwise, check thresholds from highest to lowest and print the matching grade
If none of the grade thresholds match, print Fail Flow of the complete grading example from input to final output.
#include <iostream>
int main()
{
int marks;
std::cout << "Enter marks: ";
std::cin >> marks;
if (marks < 0 || marks > 100)
{
std::cout << "Invalid marks";
}
else if (marks >= 90)
{
std::cout << "Grade A+";
}
else if (marks >= 80)
{
std::cout << "Grade A";
}
else if (marks >= 70)
{
std::cout << "Grade B";
}
else if (marks >= 60)
{
std::cout << "Grade C";
}
else
{
std::cout << "Fail";
}
return 0;
}Input
|
v
Is marks invalid?
|
+---- Yes -> Invalid
|
No
|
v
marks >= 90?
|
+---- Yes -> A+
|
No
|
v
marks >= 80?
|
+---- Yes -> A
|
No
|
v
...
|
v
FailQuick Reference
A quick reference summarizing every decision-making construct and operator covered in this topic.
Construct | Purpose
----------|--------------------------------------
if | Execute code when condition is true
if-else | Choose between two paths
else-if | Choose among multiple conditions
Nested if | Make dependent decisions
switch | Select among fixed case values
case | Defines a switch alternative
break | Exits the switch
default | Handles unmatched switch values
&& | Logical AND
|| | Logical OR
! | Logical NOT
?: | Select between two expressionsMental Model
Think of decision making as a control-flow gate: an expression is evaluated, a path is chosen, that path executes, and then execution continues.
The general mental model that underlies every decision-making construct in C++.
The core idea is simple: evaluate a condition, choose a path, execute that path, and continue execution.
What is the difference between if-else and an else-if chain in terms of how many branches can ultimately execute?
Both execute at most one branch. In a plain if-else there are only two possible paths (true/false). In an else-if chain, conditions are checked in order and only the first one that evaluates to true runs its block; once a match is found, the remaining else-if and else branches are skipped entirely, even if a later condition would also be true.
Given int a = 5, b = 15;, does the condition a > 10 && b < 20 evaluate b < 20? Explain using short-circuit evaluation.
No. Because && short-circuits, C++ evaluates the left operand first. a > 10 is false (5 is not greater than 10), and since both operands of && must be true for the whole expression to be true, the compiler does not need to evaluate b < 20 at all — the overall result is already known to be false.
In a switch statement, what happens if a case has no break statement, and why might a developer deliberately rely on this?
Without break, execution falls through into the statements of the next case (and continues doing so until it hits a break or the end of the switch). This fallthrough is sometimes intentional — for example, grouping case 6: case 7: so that both values share the same following code — and can be documented explicitly with the [[fallthrough]] attribute.
Why does C++ not evaluate the expression 0 < x < 10 as a mathematical range check, and what is the correct way to write that range check?
0 < x < 10 is parsed left to right: (0 < x) < 10. The first comparison produces a bool (true or false), which is then implicitly converted to 1 or 0 and compared against 10 — a comparison that is almost always true regardless of x. The correct way to check a range is with two explicit comparisons joined by &&, such as x > 0 && x < 10.
When is switch generally preferable to an if-else chain, and when is if-else the better choice?
switch is preferable when one expression is compared for equality against several fixed, compile-time constant values (an integral, enumeration, or convertible class type) — it reads clearly for menu-like or fixed-option logic and supports fallthrough. if-else is preferable for ranges, complex boolean expressions, or conditions that are not simple equality checks against constants, since switch case labels cannot express those directly.
What does this code print, and why?
It prints BC. value matches case 2, so execution starts there and prints B. Since case 2 has no break, execution falls through into case 3, printing C. case 3 does have a break, so the switch exits there and default is never reached.
What value does this code print for callCount, assuming the snippet is embedded in a runnable program?
It prints 0. Because || short-circuits and isConnected is already true, reconnect() is never called, so callCount stays at 0 (it would only become 1 if isConnected were false).
Write a program that reads an integer representing a day of the week (1 for Monday through 7 for Sunday) and uses a switch statement to print "Weekday" for days 1 through 5 and "Weekend" for days 6 and 7, relying on intentional fallthrough to group the cases, with a default case that prints "Invalid day" for any other value.
Cases 1 through 5 have no statements of their own, so they intentionally fall through into the shared "Weekday" output; the same grouping technique is used for 6 and 7 with "Weekend". Any value outside 1-7 falls to default and prints "Invalid day". Each group ends with break so it does not fall into the next group.
Write a program that reads a person's age and, using nested if statements, prints "Child" if age is under 13, "Teenager" if age is 13 to 19 inclusive, or "Adult" if age is 20 or older — but structure it so the teenager check is nested inside a check for "age is at least 13", to practice nested if rather than an else-if chain.
The outer if checks age < 13 for Child. Its else branch runs only when age is 13 or more, and inside that else branch a nested if checks age <= 19 to decide between Teenager and Adult. This mirrors the outline's point that an inner condition is only evaluated once an outer condition has already been resolved.
Expression
|
v
+---------+
| Evaluate|
| condition|
+---------+
|
+------+------+
| |
true false
| |
v v
Branch A Branch B
| |
+------+------+
|
v
Continueint value = 2;
switch (value)
{
case 1:
std::cout << "A";
case 2:
std::cout << "B";
case 3:
std::cout << "C";
break;
default:
std::cout << "D";
}bool isConnected = true;
int callCount = 0;
bool reconnect() { callCount++; return true; }
if (isConnected || reconnect())
{
std::cout << callCount;
}