C++ Selection

Fri Aug 29 2025
Facebook share linkTwitter/X share linkLinkedIn share linkReddit share linkReddit share link

Each program written this far has followed sequential instructions that execute one after another. Control structures break up this sequential code, allowing for code to run conditionally or repeatedly. A control structure is a programming construct that determines the flow of execution of instructions in a program. Selection is a type of control structure that allows a program to choose between different paths of execution based on whether a condition is true or false. To make selections computers use boolean expressions with selection control structures to conditionally run code.

There are 5 types of selection in C++: one-way selection, two-way selection, multiple selection (also called n-way selection), switch statements, and ternary statements.


One-Way Selection

One-way selection is the simplest form of selection and is a single block of code that executes only if a condition is true. In general, one-way selection takes the form:

if (booleanExpression) { // code to run when booleanExpression is true } // code to run after the selection (in both true/false cases)

When the booleanExpression is true it causes the block of code that follows to be run. Otherwise the block is skipped over. In both cases the code eventually meets up back in the same spot just after the block of code, unless the block of code has instructions to not run the code after the block (which would cause the true case to end somewhere else -- more on this as examples develop).

For example, the following program outputs “even number” when an even number is entered, then regardless of if the value is even or not outputs how many times that number is divisible by 2:

#include <iostream> using namespace std; int main() { // variables int num = 0; // get num from user cout << "Enter a number: "; cin >> num; // output if num is even if (num % 2 == 0) { cout << "even number\n"; } // output how many times 2 goes into num cout << "2 goes into " << num << " " << num / 2.0 << " times.\n"; return 0; }

When a number is mod by 2 the only results possible are 0 when the number is even (no remainder when 2 can divide perfectly) and 1 when the number is odd (remainder when 2 can’t divide perfectly). So when the value received from num % 2 is 0 then the value is even and the even number message is shown, otherwise the block of code is skipped over. In both cases the program continues on after the selection to output how many times 2 goes into the value entered.


Example: Validating Input

Consider the following program:

#include <iostream> using namespace std; int main() { // variables int num = 0; // get num from user cout << "Enter a number: "; cin >> num; // output if num is even cout << “You entered:<< num << endl; return 0; }

Validate the value entered to num is a valid integer.

If this was a string input, anything the user inputs would be able to be held since strings hold 0 or more characters (which all keyboard values are). For numerical inputs this is not the case. Numerical inputs can only hold numbers, but what if a user enters a value such as ”error”? It turns out that the program just fails to place the input into the num variable and leaves it in the input stream. The program then continues with its execution:

alex ~ % ./a.out Enter a number: Hello You entered: 0

The program receives the value ”Hello” into the input stream:

// SHOW STRING IN INPUT STREAM

But when the program tries to place the value into the num variable it fails. Since the value can not be placed into the variable it is left in the input stream:

// SHOW STRING FAILED TO GO IN VARIABLE AND STUCK IN INPUT STREAM

This is called input failure, or when the program tries to read input but the input does not match the expected type or format. The cin statement can be checked for input failure using:

cin.fail()

Which can be checked following a single input:

// get num from user int num = 0; cout << "Enter a number: "; cin >> num; // output if input failed cout << cin.fail() << endl; }

This will cause 0 (false) to be output if a number was entered (even if it was a floating-point as implicit typecasting gets rid of the decimal), or 1 (true) to be output if a string were to be entered. This can be incorporated into the program by checking for input failure after num is entered in a one-way selection. If input failure happens on the num then the program can output that an error occurred and quit the program:

#include <iostream> using namespace std; int main() { // variables int num = 0; // get num from user cout << "Enter a number: "; cin >> num; // check for input failure if (cin.fail()) { cout << “invalid number\n”; return 0; } // output if num is even cout << “You entered:<< num << endl; return 0; }

If input failure occurs then the one-way selection is true causing the block to code. The program will output invalid number\n and terminate the program early with the return 0. Since the block of code terminates the program at the end there is no issue that it may output the entered result. If there were no return 0 at the end of the block this would be an issue that would have to be addressed with further selections.

If on the other hand there is no input failure the one-way selection will be false, causing the block of code to be skipped over and the program to not be terminated early. Now the program will output You entered: followed by the value entered, then terminate the program at the end of the code.

The reason why failed input terminates the program early rather than recover from the failed input is because repetition would be needed. Repetition has not been gone over yet and will be gone over later. At that time the return 0 will be removed and more code added to recover from the failed input.


Example: Validating Input Incorrectly

Consider the following program:

#include <iostream> using namespace std; int main() { // variables int num1 = 0, num2 = 0; // get num1 from user cout << "Enter a number: "; cin >> num1; // get num2 from user cout << "Enter another number: "; cin >> num2; // check for input failure if (cin.fail()) { cout << “invalid number\n”; return 0; } // output the 2 numbers added together cout << num1 << " + " << num2 << " = " << num1 + num2 << endl; return 0; }

If input failure happens on the second input (num2) the check will succeed:

alex ~ % ./a.out Enter a number: 20 Enter another number: error invalid number

But if the failure happens on the first input (`num1) the check will fail:

alex ~ % ./a.out Enter a number: error Enter another number: invalid number

The second input does not allow the user to enter anything and the invalid number message is on the same line as the prompt for the second input. The input is skipped because the first input failed to place the error value into the num1 variable, so error was left in the input stream. When the second input statement is hit it does not need to get input from the user since it already has error in the input stream. So the program tries to put error into num2, fails, then outputsinvalid number. The failure message is on the same line as the prompt because there was no user to hit enter on their keyboard after the input to put the newline there.

To fix this behavior, every numerical input should have input failure checking directly after the input:

#include <iostream> using namespace std; int main() { // variables int num1 = 0, num2 = 0; // get num1 from user cout << "Enter a number: "; cin >> num1; // check for input failure on the previous input if (cin.fail()) { cout << “invalid number\n”; return 0; } // get num2 from user cout << "Enter another number: "; cin >> num2; // check for input failure on the previous input if (cin.fail()) { cout << “invalid number\n”; return 0; } // output the 2 numbers added together cout << num1 << " + " << num2 << " = " << num1 + num2 << endl; return 0; }

Now, if input failure occurs on the second input it will work properly as it did previously. But, if input failure occurs on the first input it will output the error message and terminate the program immediately after:

alex ~ % ./a.out Enter a number: error invalid number

Two-Way Selection

Two-way selection is a control structure that chooses between two possible paths of execution, depending on whether a condition is true or false. In general, two-way selection takes the form:

if (booleanExpression) { // code to run when booleanExpression is true } else { // code to run when booleanExpression is false }

Like one-way selection, when the booleanExpression is true it causes the first block of code following the if to be run. Otherwise, when the booleanExpression is false the first block of code is skipped over and the second block of code following the else is run.

For example, the following program outputs “even number” when an even number is entered, and ”odd number” when an odd number is entered. The, regardless of if the value is even or not outputs how many times that number is divisible by 2:

#include <iostream> using namespace std; int main() { // variables int num = 0; // get num from user cout << "Enter a number: "; cin >> num; // output if num is even if (num % 2 == 0) { cout << "even number\n"; } else { cout << "odd number\n"; } // output how many times 2 goes into num cout << "2 goes into " << num << " " << num / 2.0 << " times.\n"; return 0; }

Like the previous example in the one-way selection section, if num % 2 is 0 this means the value is even as 2 can divide any even number perfectly. Otherwise, the only other result is 1 which means the value is odd.

So when num % 2 == 0 in the if is true this means the number is even, and the true triggers the code after the if to run which outputs "even number\n". Otherwise, the expression is false which causes the first block of code to be skipped over and the second block of code after the else to be run, which outputs "odd number\n".

To slightly clean up this check the == 0 can be removed and blocks of code swapped:

#include <iostream> using namespace std; int main() { // variables int num = 0; // get num from user cout << "Enter a number: "; cin >> num; // output if num is even if (num % 2) { cout << "odd number\n"; } else { cout << "even number\n"; } // output how many times 2 goes into num cout << "2 goes into " << num << " " << num / 2.0 << " times.\n"; return 0; }

Remember, 1 is true and 0 is false. And num % 2 can only return two values, 0 when the value is even and 1 when the value is odd. So the even case being 0 correlates to false and the odd case being 1 correlates to true. So when the result of num % 2 is 1 (true/odd) the value can be used as the boolean value passed to the if statement as the 1 is just implicitly typecast to true, causing the first block of code to be run which outputs cout << "odd number\n". Otherwise, the result will be 0 (false/even) causing the second block of code to be run which outputs cout << "even number\n".


Example: Logging In

Write a program that reads in a username and password, checks if the username is ”Ronald” and the password is ”McDonald”, and if the credentials are correct outputs ”logged in” otherwise outputs ”invalid credentials”.

First, the username and password must be read into string variables:

#include <iostream> using namespace std; int main() { // variables string username = “”, password = “”; // get username cout << “Username:; getline(cin, username); // get password cout << “Password:; getline(cin, password); return 0; }

The getline() instruction is used as the username and password may have spacing in them, and if cin >> was used that spacing or anything after it would not be read in. No input failure needs to be added after each input as strings are being used for the variable types so anything from the keyboard can be held in a string. Once the username and password are read in they can be checked for validity:

#include <iostream> using namespace std; int main() { // variables/constants string username = “”, password = “”; const string REQ_USERNAME = “Ronald”, REQ_PASSWORD = “McDonald”; // get username cout << “Username:; getline(cin, username); // get password cout << “Password:; getline(cin, password); // validate credentials if (username == REQ_USERNAME && password == REQ_PASSWORD) { cout << “logged in\n”; } else { cout << “invalid credentials\n”; } return 0; }

Constants for REQ_USERNAME and REQ_PASSWORD are added and checked against the variables holding the entered username and password. Both the username must be equivalent to the REQ_USERNAME and password equivalent to the REQ_PASSWORD at the same time for the credentials to be valid, thus the logical AND is used as both sides of the logical AND must be true for the first block of code after the if to run. If the credentials are valid “logged in\n” is output from the if case, otherwise the credentials are invalid and “invalid credentials\n” is output from the else case.


Example: String Bounds

To the Logging In example program add that the username must have between 3 and 12 characters (inclusive) and the password between 6 and 18 characters.

Since the username and password are both strings their length can be checked using:

string.length()

Where string is any string variable. Thus, after each input is received, its length can be checked against the required ranges and an error output if either is out of range:

#include <iostream> using namespace std; int main() { // variables/constants string username = “”, password = “”; const string REQ_USERNAME = “Ronald”, REQ_PASSWORD = “McDonald”; // get username cout << “Username:; getline(cin, username); // make sure username is valid length if (username.length() < 3 || username.length() > 12) { cout << “invalid username (must be: 3 <= length <= 12)\n”; return 0; } // get password cout << “Password:; getline(cin, password); // make sure password is valid length if (password.length() < 6 || password.length() > 18) { cout << “invalid password (must be: 6 <= length <= 18)\n”; return 0; } // validate credentials if (username == REQ_USERNAME && password == REQ_PASSWORD) { cout << “logged in\n”; } else { cout << “invalid credentials\n”; } return 0; }

After the username is read it is checked if it is either too small or too large, and if either case is true it triggers the block of code that follows to run outputting “invalid password (must be: 6 <= length <= 18)\n” and terminating the program. Otherwise if both checks are false then the block of code is skipped and the program moves on to reading in the password. The password is then validated using the same logic as the username.


N-Way (Multiple) Selection

Often a selection may have several cases, such as in video games when multiple spells are selected through. In these instances n-way (multiple) selection--a control structure that allows a program to choose between more than 2 possible execution paths--can be employed. Multiple selection allows any amount of instruction sets to be blocked off with an expression that must be true in order to run. In general multiple selection takes the form:

if (booleanExpression1) { // code to run when booleanExpression1 is true } else if (booleanExpression2) { // code to run when booleanExpression2 is true } ... // as many else if’s as needed else { statements; }

Just like one-way and two-way selection, when booleanExpression1 is true the first block of code runs and all the else if and else in the selection are skipped over, otherwise it moves on to the else. This time the else has a if attached to it. The if just allows a condition to be checked, so when attached to an else it just means to run that else case the condition attached must be true. So booleanExpression2 is tested and if is true then the second block of code is run, otherwise the selections can stop here or another else can be attached. That else can or can not have an else depending on the selection, and this can go on for as many cases as the selection needs.

For example, to select what recipe to show, the user would first be asked for the name of a recipe, then as many cases as recipes can be added as needed using else if:

#include <iostream> using namespace std; int main() { // variables string recipe = “”; // get username cout << “What do you want to cook?; getline(cin, recipe); // mom’s spaghetti if (recipe == “spaghetti”) { cout << “The recipe for mom’s spaghetti is...\n"; } // pizza else if (recipe == “pizza”) { cout << “The recipe for pizza is...\n"; } // invalid recipe else { cout << “Invalid recipe\n"; } return 0; }

When the user enters spaghetti then recipe == “spaghetti” is true causing the first block of code to run which outputs “The recipe for mom’s spaghetti is...\n", skipping the next 2 blocks of code. If the user enters pizza then the first check is false and recipe == “pizza” is true causing the second block of code to run which outputs “The recipe for pizza is...\n", skipping the first and final blocks of code. If the user enters anything else then both of the checks will be false, which causes the first two blocks of code to be skipped and the final else case which outputs “Invalid recipe\n" to be run by default.


Example: Splitting Bounds Checks

To the String Bounds example add outputting separate too small/large messages for the lengths of the username/ password.

Recall the checks currently are in the form:

if (length < lowBound || length > upBound) { cout << “invalid”; return 0; }

Currently if either side of the logical OR is true (too small or too big) the the statement is true and the error message is output. To output separate messages the logical OR needs to be split into two cases that output different messages depending if the case is true:

if (length < lowBound) { cout << “too small”; return 0; } else if (length > upBound) { cout << “too large”; return 0; }

This logic can be applied to the program from the String Bounds example for each input that is being error checked:

#include <iostream> using namespace std; int main() { // variables/constants string username = “”, password = “”; const string REQ_USERNAME = “Ronald”, REQ_PASSWORD = “McDonald”; // get username cout << “Username:; getline(cin, username); // make sure username is big enough if (username.length() < 3) { cout << “invalid username (must be: length >= 3)\n”; return 0; } // make sure username is small enough else if (username.length() > 12) { cout << “invalid username (must be: length <= 12)\n”; return 0; } // get password cout << “Password:; getline(cin, password); // make sure password is big enough if (password.length() < 6) { cout << “invalid password (must be: length >= 6)\n”; return 0; } // make sure password is small enough else if (password.length() > 18) { cout << “invalid password (must be: length <= 18)\n”; return 0; } // validate credentials if (username == REQ_USERNAME && password == REQ_PASSWORD) { cout << “logged in\n”; } else { cout << “invalid credentials\n”; } return 0; }

Now if the username is too small then username.length() < 3 will be true which will cause its block of code to be run which outputs “invalid username (must be: length >= 3)\n” and terminates the program. Otherwise the first block of code is skipped over and the else is run, which has a condition to run. If the username is too large then username.length() > 12 will be true which will cause its block of code to be run which outputs “invalid username (must be: length <= 12)\n” and terminates the program. If the lengths are valid, then the program moves on to the password, which has error checking that follows the same logic with different bounds and error messages.

However, this is not the best use case of multiple selection as the program is being terminated inside of each block of code. Thus, the program gets terminated before it ever has the chance to move on to skipping the else if so the else is not necessary:

#include <iostream> using namespace std; int main() { // variables/constants string username = “”, password = “”; const string REQ_USERNAME = “Ronald”, REQ_PASSWORD = “McDonald”; // get username cout << “Username:; getline(cin, username); // make sure username is big enough if (username.length() < 3) { cout << “invalid username (must be: length >= 3)\n”; return 0; } // make sure username is small enough if (username.length() > 12) { cout << “invalid username (must be: length <= 12)\n”; return 0; } // get password cout << “Password:; getline(cin, password); // make sure password is big enough if (password.length() < 6) { cout << “invalid password (must be: length >= 6)\n”; return 0; } // make sure password is small enough if (password.length() > 18) { cout << “invalid password (must be: length <= 18)\n”; return 0; } // validate credentials if (username == REQ_USERNAME && password == REQ_PASSWORD) { cout << “logged in\n”; } else { cout << “invalid credentials\n”; } return 0; }

When there is error for too small of a value in either the username or password inputs then the program is terminated, there is no worry for the program continuing on to further code it should not. If there is no error for too small of a value the block of code is automatically skipped over and the next independent selection is done to see if an error/termination needs to happen for the value being too big. Otherwise if there are no errors in the input then the program has valid values and can continue on with execution.

A better use of multiple selection would be having the user make a selection between multiple options.


Example: Selecting Spell to Cast

Write a program that reads in the user’s magic level (integer between 3 and 99), then asks for a spell to cast between fire bolt, fire blast, and fire wave and reads the spell name into a string, then outputs the name of the spell entered or that the selection is invalid.

First, the program needs to read in the user’s magic level and guarantee it is between 3 and 99:

#include <iostream> using namespace std; int main() { // variables int magicLvl = 0; // get magic level cout << “Magic Level:; cin >> magicLvl; // make sure magic level is in range if (magicLvl < 3 || magicLvl > 99) { cout << “invalid magic level (must be 3 <= level <= 99)\n”; return 0; } return 0; }

Like the examples before the range needs to be checked and the program terminated if the input goes out of range. This time a numerical value is being read in though so input failure can happen and needs to be checked for:

#include <iostream> using namespace std; int main() { // variables int magicLvl = 0; // get magic level cout << “Magic Level:; cin >> magicLvl; // make sure magic level is in range if (cin.fail() || magicLvl < 3 || magicLvl > 99) { cout << “invalid magic level (must be 3 <= level <= 99)\n”; return 0; } return 0; }

Once a valid value in range is guaranteed to have been read in the in then the program can ask to choose between fire bolt, fire blast, and fire wave:

#include <iostream> using namespace std; int main() { // variables int magicLvl = 0; string spell = “”; // get magic level cout << “Magic Level:; cin >> magicLvl; // make sure magic level is in range if (cin.fail() || magicLvl < 3 || magicLvl > 99) { cout << “invalid magic level (must be 3 <= level <= 99)\n”; return 0; } // get spell selection cout << “Which spell:\n fire bolt\n fire blast\n fire wave\nSelection:; getline(cin, spell); return 0; }

The spell is a string so it does not need to be error checked for input failure. It also does not need to be directly checked if a valid spell was entered because checking if it is a valid spell will be handled by multiple selection. There is one problem though, the user will not be able to enter anything for the spell. As written the program will currently produce the following interaction:

alex ~ % ./a.out Magic Level: 99 Which spell: fire bolt fire blast fire wave Selection: alex ~ %

The user will be able to enter a value for the magicLvl but the spell input will be skipped over because recall from when input was first discussed, the stream extraction operator leaves the ’\n’ in the input stream and when a new stream extraction operator is called it automatically removes it. This is not the case for getline() as getline() removes the ’\n’ from the input stream, so when a new getline() is called it does not remove anything prior to reading. Thus if anything is in the input stream, such as the ’\n’ from cin >> magicLvl then it must be ignored:

#include <iostream> using namespace std; int main() { // variables int magicLvl = 0; string spell = “”; // get magic level cout << “Magic Level:; cin >> magicLvl; cin.ignore(256, '\n'); // make sure magic level is in range if (cin.fail() || magicLvl < 3 || magicLvl > 99) { cout << “invalid magic level (must be 3 <= level <= 99)\n”; return 0; } // get spell selection cout << “Which spell:\n fire bolt\n fire blast\n fire wave\nSelection:; getline(cin, spell); return 0; }

Now, the spell will be able to be read in. To start with the spell selection add 4 separate independent selections after selecting the spell, one for each of the spells and one for an invalid selection:

#include <iostream> using namespace std; int main() { // variables int magicLvl = 0; string spell = “”; // get magic level cout << “Magic Level:; cin >> magicLvl; cin.ignore(256, '\n'); // make sure magic level is in range if (cin.fail() || magicLvl < 3 || magicLvl > 99) { cout << “invalid magic level (must be 3 <= level <= 99)\n”; return 0; } // get spell selection cout << “Which spell:\n fire bolt\n fire blast\n fire wave\nSelection:; getline(cin, spell); // fire bolt if (spell == “fire bolt”) { // code for fire bolt } // fire blast if (spell == “fire blast”) { // code for fire blast } // fire wave if (spell == “fire wave”) { // code for fire wave } // invalid selection if (/*what would go here?*/) { // code for invalid selection } return 0; }

For each of the spells there would be no issue, the program would look at the spell, and if it sees one that matches one of the first 3 spell checks then it will run the code that is eventually put there for the spell cast. But what about the invalid selection? What boolean expression would go where the comment /*what would go here?*/ is? It would be an infinitely long boolean expression saying every permutation of keystrokes the user could possibly enter with the keyboard all logical ORed together.

A much simpler way to say this would just be to say if it’s not one of the previous 3 then just output that it’s invalid. This is what multiple selection is for. Since the else attaches a block of code to the false case of an if the 4 blocks can just be strung together with else with extra conditions on them as needed. So rather than have 4 separate independent selections, have one multiple selection:

#include <iostream> using namespace std; int main() { // variables int magicLvl = 0; string spell = “”; // get magic level cout << “Magic Level:; cin >> magicLvl; cin.ignore(256, '\n'); // make sure magic level is in range if (cin.fail() || magicLvl < 3 || magicLvl > 99) { cout << “invalid magic level (must be 3 <= level <= 99)\n”; return 0; } // get spell selection cout << “Which spell:\n fire bolt\n fire blast\n fire wave\nSelection:; getline(cin, spell); // fire bolt if (spell == “fire bolt”) { // code for fire bolt } // fire blast else if (spell == “fire blast”) { // code for fire blast } // fire wave else if (spell == “fire wave”) { // code for fire wave } // invalid selection else { // code for invalid selection } return 0; }

Now the program will only go on to the later checks and potentially run their blocks of code once the respective earlier checks are run, otherwise they will be skipped over. And in the final case of invalid selection no boolean expression is needed as if it's not one of the 3 then it's invalid.


Example: Casting a Spell

To the example on Selecting a Spell to Cast add calculating and outputting the max hit for the spell selected based on the formula maxHit=int(baseDmg(1+magicLvl100))maxHit=int(baseDmg * (1 + \frac{magicLvl}{100})). For baseDmg use 12 for fire bolt, 16 for fire blast, and 20 for fire wave.

In the program each of the 3 blocks of code for the spells could simply calculate the max hit based on the base damage for the spell then output it:

#include <iostream> using namespace std; int main() { // variables int magicLvl = 0, maxDamage = 0; string spell = “”; // get magic level cout << “Magic Level:; cin >> magicLvl; cin.ignore(256, '\n'); // make sure magic level is in range if (cin.fail() || magicLvl < 3 || magicLvl > 99) { cout << “invalid magic level (must be 3 <= level <= 99)\n”; return 0; } // get spell selection cout << “Which spell:\n fire bolt\n fire blast\n fire wave\nSelection:; getline(cin, spell); // fire bolt if (spell == “fire bolt”) { maxDamage = 12 * (1 + magicLvl / 100.0); cout << spell << “ max damage is “ << maxDamage << endl; } // fire blast else if (spell == “fire blast”) { maxDamage = 16 * (1 + magicLvl / 100.0); cout << spell << “ max damage is “ << maxDamage << endl; } // fire wave else if (spell == “fire wave”) { maxDamage = 20 * (1 + magicLvl / 100.0); cout << spell << “ max damage is “ << maxDamage << endl; } // invalid selection else { cout << “invalid spell\n”; } return 0; }

This works but it can be simplified. The only thing changing between the 3 blocks of code for the spells is the constant being multiplied in the formula at the end. Besides that the calculation and output are the same. Rather than repeat the same code over and over and over a variable can just be set to the value needed to be multiplied in the formula, then after the selection the formula can be calculated and results output:

#include <iostream> using namespace std; int main() { // variables int magicLvl = 0, maxDamage = 0, baseDmg = 0; string spell = “”; // get magic level cout << “Magic Level:; cin >> magicLvl; cin.ignore(256, '\n'); // make sure magic level is in range if (cin.fail() || magicLvl < 3 || magicLvl > 99) { cout << “invalid magic level (must be 3 <= level <= 99)\n”; return 0; } // get spell selection cout << “Which spell:\n fire bolt\n fire blast\n fire wave\nSelection:; getline(cin, spell); // fire bolt if (spell == “fire bolt”) { baseDmg = 12; } // fire blast else if (spell == “fire blast”) { baseDmg = 16; } // fire wave else if (spell == “fire wave”) { baseDmg = 20; } // invalid selection else { cout << “invalid spell\n”; } maxDamage = baseDmg * (1 + magicLvl / 100.0); cout << spell << “ max damage is “ << maxDamage << endl; return 0; }

Now the selection is just used to set the baseDmg equivalent to that of the spell that the user selected. The baseDmg is then used after the selection to calculate the maxDamage for the selected spell, and then output the results.

But what happens in the invalid selection case? If the user enters a string that is not one of the 3 spells the first 3 checks will be false and all of their blocks of code skipped over, causing the final else to be run. The else outputs an error message, but once it is done it continues executing the program, which will calculate the maxDamage as 0 since the initial value of baseDmg is 0 and anything multiplied by 0 is 0. That value will then be output as the result. To fix this the program needs to be terminated when the error happens:

#include <iostream> using namespace std; int main() { // variables int magicLvl = 0, maxDamage = 0, baseDmg = 0; string spell = “”; // get magic level cout << “Magic Level:; cin >> magicLvl; cin.ignore(256, '\n'); // make sure magic level is in range if (cin.fail() || magicLvl < 3 || magicLvl > 99) { cout << “invalid magic level (must be 3 <= level <= 99)\n”; return 0; } // get spell selection cout << “Which spell:\n fire bolt\n fire blast\n fire wave\nSelection:; getline(cin, spell); // fire bolt if (spell == “fire bolt”) { baseDmg = 12; } // fire blast else if (spell == “fire blast”) { baseDmg = 16; } // fire wave else if (spell == “fire wave”) { baseDmg = 20; } // invalid selection else { cout << “invalid spell\n”; return 0; } maxDamage = baseDmg * (1 + magicLvl / 100.0); cout << spell << “ max damage is “ << maxDamage << endl; return 0; }

Now when an invalid spell name is entered the program is terminated so it does not have a chance to go onto calculating or outputting any invalid results.


Example: Combining Login and Spell Select

Combine the final programs from the examples to Login and Cast a Spell into a single program. The program should have the user login prior to casting a spell.

Recall the final state of the program to login:

#include <iostream> using namespace std; int main() { // variables/constants string username = “”, password = “”; const string REQ_USERNAME = “Ronald”, REQ_PASSWORD = “McDonald”; // get username cout << “Username:; getline(cin, username); // make sure username is big enough if (username.length() < 3) { cout << “invalid username (must be: length >= 3)\n”; return 0; } // make sure username is small enough if (username.length() > 12) { cout << “invalid username (must be: length <= 12)\n”; return 0; } // get password cout << “Password:; getline(cin, password); // make sure password is big enough if (password.length() < 6) { cout << “invalid password (must be: length >= 6)\n”; return 0; } // make sure password is small enough if (password.length() > 18) { cout << “invalid password (must be: length <= 18)\n”; return 0; } // validate credentials if (username == REQ_USERNAME && password == REQ_PASSWORD) { cout << “logged in\n”; } else { cout << “invalid credentials\n”; } return 0; }

Which ends with checking username == REQ_USERNAME && password == REQ_PASSWORD to see if the entered credentials are correct, and if they are this statement is true which causes ”logged in\n” to be output. Otherwise ”invalid credentials” is output. But in an actual system something will actually happen after the login, and as written if code was written after the final selection then even in the invalid credentials case the code would be run. To easily fix this the De Morgan’s Law can be applied to the expression so invalid credentials make the expression true, the ”invalid code” message can then be output and the program terminated from the first block in the selection. Then there is no need to add a second block for the valid credentials case because the code that runs after the selection is the valid credentials case:

#include <iostream> using namespace std; int main() { // variables/constants string username = “”, password = “”; const string REQ_USERNAME = “Ronald”, REQ_PASSWORD = “McDonald”; // get username cout << “Username:; getline(cin, username); // make sure username is big enough if (username.length() < 3) { cout << “invalid username (must be: length >= 3)\n”; return 0; } // make sure username is small enough if (username.length() > 12) { cout << “invalid username (must be: length <= 12)\n”; return 0; } // get password cout << “Password:; getline(cin, password); // make sure password is big enough if (password.length() < 6) { cout << “invalid password (must be: length >= 6)\n”; return 0; } // make sure password is small enough if (password.length() > 18) { cout << “invalid password (must be: length <= 18)\n”; return 0; } // validate credentials if (username != REQ_USERNAME || password != REQ_PASSWORD) { cout << “invalid credentials\n”; return 0; } // code for when valid credentials are entered return 0; }

When either or both the username or password are incorrect, then the program is terminated in the block of code so no further code will be executed. Then the code for when valid credentials are entered can be placed where the // code for when valid credentials are entered comment is, which is the code for the solution to the Casting a Spell example:

#include <iostream> using namespace std; int main() { // variables/constants int magicLvl = 0, maxDamage = 0, baseDmg = 0; string username = “”, password = “”, spell = “”; const string REQ_USERNAME = “Ronald”, REQ_PASSWORD = “McDonald”; // get username cout << “Username:; getline(cin, username); // make sure username is big enough if (username.length() < 3) { cout << “invalid username (must be: length >= 3)\n”; return 0; } // make sure username is small enough if (username.length() > 12) { cout << “invalid username (must be: length <= 12)\n”; return 0; } // get password cout << “Password:; getline(cin, password); // make sure password is big enough if (password.length() < 6) { cout << “invalid password (must be: length >= 6)\n”; return 0; } // make sure password is small enough if (password.length() > 18) { cout << “invalid password (must be: length <= 18)\n”; return 0; } // validate credentials if (username != REQ_USERNAME || password != REQ_PASSWORD) { cout << “invalid credentials\n”; return 0; } // get magic level cout << “Magic Level:; cin >> magicLvl; cin.ignore(256, '\n'); // make sure magic level is in range if (cin.fail() || magicLvl < 3 || magicLvl > 99) { cout << “invalid magic level (must be 3 <= level <= 99)\n”; return 0; } // get spell selection cout << “Which spell:\n fire bolt\n fire blast\n fire wave\nSelection:; getline(cin, spell); // fire bolt if (spell == “fire bolt”) { baseDmg = 12; } // fire blast else if (spell == “fire blast”) { baseDmg = 16; } // fire wave else if (spell == “fire wave”) { baseDmg = 20; } // invalid selection else { cout << “invalid spell\n”; return 0; } maxDamage = baseDmg * (1 + magicLvl / 100.0); cout << spell << “ max damage is “ << maxDamage << endl; return 0; }

When valid credentials are entered the program then enters into the Casting a Spell part of the example where the code simply needs to be copy/pasted without edits. Since the program terminates when invalid credentials are entered it is not necessary to guard off the code with any further selections which makes the code simpler than had more selections been added.

A final successful interaction with the program follows:

Username: Ronald Password: McDonald Magic Level: 99 Which spell: fire bolt fire blast fire wave Selection: fire bolt fire bolt max damage is 23

When the user enters the valid credentials Ronald and McDonald then the program continues onto the Casting a Spell part of the example where 99 and fire bolt are entered which produces the output fire bolt max damage is 23.


Switch Statements

Switch statements are a control structure that chooses one case from many possible cases based on the value of an integral expression. An integral expression is an expression that evaluates to an integral value, which means it can evaluate to the types:

  1. int, short, long long and their unsigned versions.

  2. char

  3. bool

In general, switch statements take the form:

switch (integralExpression) { case 1: // case instructions break; case 2: // case instructions break; ... case n: // case instructions break; default: // case instructions }

The integralExpression evaluates to an integral value which can match to a case. Each value 1, 2, ..., n in each respective case needs to be changed to values of the type that the integralExpression will evaluate to based on what values need to be handled for the use case. Then when the integralExpression is evaluated whatever it resolves to may match up with a case causing the code for the case to run. If no case matches then the default case allows for code to be run. The default case can be used or left out depending on the use case.

For example, consider the following snippet of code:

int x = 1; switch (x) { case 1: cout << "Case 1 executed\n"; break; default: cout << "Default executed\n"; }

The variable x is an integer which is an integral variable that can be used in a switch statement as the statement’s expression. Since x = 1 the expression matches to case 1 which causes "Case 1 executed\n" to be output followed by the switch being broken out of using the break statement. The break statement tells the program to stop executing the switch statement which causes the program to continue execution after the block of code for the switch. If however x = 50 or any value other than 1 then there would be no case for the expression to match except the default which causes "Default executed\n" to be output. The default does not need a break after it because the next thing that comes after it is the end of the block of code for the switch.

But what happens if the break is left out:

int x = 1; switch (x) { case 1: cout << "Case 1 executed\n"; default: cout << "Default executed\n"; }

When run this snippet will produce the output:

Case 1 executed Default executed

Both messages are output! This is because the break is what causes the switch to stop being executed at the end of each case. If a case has no break then the switch falls through to the next case until a break is hit, known as fall-through. This can be very useful in selections where the same code has to be run for multiple cases, but in this use case it is important to not forget the break otherwise the program will output unintended results.


Improper Use of Switch Statements

Since boolean values are integral they can be used in switch statements:

bool b = true; switch (b) { case true: cout << "b was true\n"; break; default: cout << "b was false\n"; }

But when this is done there are only 2 cases which can run: true or false so a switch statement is overkill. This would be the perfect use case for 2 way selection:

bool b = true; if (b) { cout << "b was true\n"; } else { cout << "b was false\n"; }

In general it is best to use 1/2/n-way selection as most selections are based on boolean expressions. When the selection should be converted over to a switch statement is when there are many integral values checks that are being logical ORed together to trigger a block of code to be run. In this case fall-through can be used rather than the logical ORs to make the code cleaner.


Example: Selecting Calculation with Switch

Use a switch statement to calculate/output area or perimeter of a square based on a user’s selection.

First, the side length of the square needs to be read and error checked:

#include <iostream> using namespace std; int main() { // variables double side = 0.0; // read side length cout << “Side length:; cin >> side; // error checks if (cin.fail() || side <= 0) { cout << “Invalid side length\n”; return 0; } return 0; }

If the side length read in is not a number or is not positive, as a side must have some length to be a square, then it is not a valid side length. The user’s selection for area or perimeter can then be read:

#include <iostream> using namespace std; int main() { // variables double side = 0.0; char selection = 0; // read side length cout << “Side length:; cin >> side; // error checks if (cin.fail() || side <= 0) { cout << “Invalid side length\n”; return 0; } // read calculation selection cout <<(A/a)rea -or- (P/p)erimeter:; cin >> selection; return 0; }

Since the selection is a character which is an integral data type, its value can be switched on calculating area if a or A is entered and perimeter if p or P is entered:

#include <iostream> using namespace std; int main() { // variables double side = 0.0; char selection = 0; // read side length cout << “Side length:; cin >> side; // error checks if (cin.fail() || side <= 0) { cout << “Invalid side length\n”; return 0; } // read calculation selection cout <<(A/a)rea -or- (P/p)erimeter:; cin >> selection; switch (selection) { case 'a': area = side * side; cout << "area = " << area << endl; break; case 'A': area = side * side; cout << "area = " << area << endl; break; case 'p': perimeter = side * 4; cout << "perimeter = " << perimeter << endl; break; case 'P': perimeter = side * 4; cout << "perimeter = " << perimeter << endl; break; default: cout << "Invalid selection\n"; } return 0; }

If the user enters a, A, p, or P the proper value will be calculated and its result output. Otherwise if anything else is entered "Invalid selection\n" will be output.

But this can be simplified. Consider the following:

switch (selection) { case 'a': area = side * side; cout << "area = " << area << endl; case 'A': area = side * side; cout << "area = " << area << endl; break; }

If selection is a then the area will be calculated and output twice since there is no break. So if the code from case ‘a’ was completely removed:

switch (selection) { case 'a': case 'A': area = side * side; cout << "area = " << area << endl; break; }

When selection is a it will just run the code for case ‘A’, calculating the area and outputting it only once in the case that selection is a. This logic can be used for the perimeter as well:

#include <iostream> using namespace std; int main() { // variables double side = 0.0; char selection = 0; // read side length cout << “Side length:; cin >> side; // error checks if (cin.fail() || side <= 0) { cout << “Invalid side length\n”; return 0; } // read calculation selection cout <<(A/a)rea -or- (P/p)erimeter:; cin >> selection; switch (selection) { case 'a': case 'A': area = side * side; cout << "area = " << area << endl; break; case 'p': case 'P': perimeter = side * 4; cout << "perimeter = " << perimeter << endl; break; default: cout << "Invalid selection\n"; } return 0; }

Now, if the user enters a or A the area will be calculated and output from case ‘A’, and if they enter p or P the perimeter will be calculated and output from case ‘P’. If any other value is entered then "Invalid selection\n" will be output.

Nesting Selections

Nesting is when one control structure is placed inside of another control structure. In general, selections can be nested inside of one another in any arrangement:

if (expression1) { if (expression2) { statement; } ... // can have else if/else switch(expression3) { ... } ... } ... // can have else if/else with nesting

For example, to be able to gamble a person must be 21:

age = 20; if (if age >= 21) { cout << “Allowed to gamble\n”; }

But on people’s 21st birthday the casino wants the user to be greeted with a happy birthday message, followed by an ad to sign up for a player’s card. Inside of the block of code it is already known that the person is at least 21 years of age, but another check can be nested inside to check if the user is exactly 21:

age = 21; if (age >= 21) { if (age == 21) { cout << “Happy birthday!\n” << “Sign up for a player’s card at the club desk!\n” << “Get a free meal at the buffet today!\n”; } cout << “Allowed to gamble\n”; }

Now if the user is 21 or older it will be displayed they are allowed to gamble, but in the case that they are 21 they will be advertised to go sign up for a player’s club card.


Terms

  1. Control Structure - A programming construct that determines the flow of execution of instructions in a program

  2. Selection - A type of control structure that allows a program to choose between different paths of execution based on whether a condition is true or false

  3. One-Way Selection - A single block of code that executes only if a condition is true.

  4. Input Failure - When the program tries to read input but the input does not match the expected type or format.

  5. Two-Way Selection - A control structure that chooses between two possible paths of execution, depending on whether a condition is true or false.

  6. N-Way (Multiple) Selection - A control structure that allows a program to choose between more than 2 possible execution paths.

  7. Switch Statement - A control structure that chooses one case from many possible cases based on the value of an integral expression.

  8. Integral Expression - An expression that evaluates to an integral value.

  9. Fall-Through - When a case has no break and a switch falls through to the next case until a break is hit.

  10. Nesting - When one control structure is placed inside of another control structure.


Questions

  1. List the five kinds of selection mentioned.

  2. When does a one-way selection run its block?

  3. After a one-way selection finishes (true or false), where does execution continue?

  4. What does two-way selection add beyond one-way selection?

  5. What is the difference between 2-way and multiple selection?

  6. In what order are expressions evaluated in multiple selection?

  7. What is an “integral expression”?

  8. What are the allowed types of a switch expression?

  9. What does default do in a switch, and is it required?

  10. What is “fall-through”?

  11. Why are breaks needed in switch cases?

  12. When are breaks needed in switch cases?

  13. Why is switch usually a poor fit for boolean conditions?

  14. What does “nesting selections” mean?

  15. Give one reason to nest an if inside another if.

  16. When would you prefer multiple selection over a switch statement?

  17. When would you prefer a switch statement over an multiple selection?

  18. How do you guarantee that exactly one branch executes among many mutually exclusive conditions?

  19. What kinds of bugs can overlapping conditions create in separate if statements? Give a brief example scenario.

  20. When several branches share the same final computation, where should the shared code live to avoid duplication?

  21. What are the pros and cons of using an early return on error versus wrapping the rest of your code in an else block?

  22. Why can’t switch use floating-point or std::string conditions, and what alternatives should you use?

  23. If you need to branch on a std::string like "fire wave", what selection structure should you use and why?

  24. When is intentional fall-through appropriate?

  25. Where can the default label appear in a switch, and does its position affect behavior?

  26. What does break do inside a switch?

  27. After cin >> n followed by getline(cin, s), why might s be empty, and how do you fix it?

  28. After a failed numeric extraction, what is the state of cin, and what happens if you keep reading without clearing it?

  29. Compare return 0; in an error branch to wrapping the rest of the code in an else. Which is easier to maintain and why?

  30. What is input failure?

  31. How is input failure checked?

  32. Why validate immediately after I/O instead of collecting several inputs first and checking at the end?

MORE TO BE ADDED SOON