Hello and welcome. What do you like to order?

“Good afternoon, I would like the Burger of the Month with Curly Fries.”

We are very sorry, but unfortunately Curly Fries are out at the moment. Are french fries okay?

“That’s too bad, but fine. I’m okay with that as an alternative.”

What drink would you like with your menu?

You can probably guess the rest of the conversation.

Dialogues of this kind are quite commonplace. From one starting point, several paths arise to different or sometimes comparable directions. But the exact course of events in most situations is not predictable and depends on several factors.

This is also true for the behavior of systems and your software. Often the next instruction depends on a condition. But how does your program react to decisions and how do you control the further course of events?

Up to now, our programs have processed all instructions serially from top to bottom. But this usually does not meet the requirements of the real world. There the external circumstances must be determined and flexibly handled on it.

Information technology knows the two concepts of conditional instructions and loops for the control of the program flow.

Selection and branching

First we look at the if ... else construct. This can be used to react to conditions in different ways. You can take the construct very literally. If if the condition occurs, then execute the following statements. Otherwise else take the other statements.

So the flow branches. Depending on whether the condition returns true or false, one of the two branches is followed up.

How does the if ... else construct look like in the source code? Introduced by the keyword if an expression follows in round brackets (), which formulates the condition. The expression is evaluated as bool. The special thing about it: 0 is interpreted as false and every different value from 0 as true.

Subsequently, in curly brackets {} come the statements that are to be executed if the condition is met. If statements belong together and are enclosed by the brackets, they are often called compound statements.

This gives you a fully compliant if statement. You can leave it as it is. If the condition is met, then the program continues into the parentheses. Otherwise, the prorgam skips the bracketed contents and continues execution afterwards.

Optionally, you can also specify what should happen if the condition is not met, i.e. false. For this you have to write an else statement directly after the if statement. Here you do not need an explicitly formulated condition. The else branch is implicitly the negation of the if condition. You only need to list your statements in curly brackets.

In listing1 you can find an example. Using the rand() function from <stdlib>, we generate a random number and then look at the remainder that comes out of the modulo operation with 2. That is, the boolean variable condition happens to have the value 0or 1.

The value of the variable is used as the condition of the if statement. If condition = true the string condition is true is output. Otherwise else the console shall display the string condition is false.

// listing1: conditional execution with if ... else

#include <iostream>
#include <stdlib.h>

int main()
{
    bool condition = rand() % 2;
    std::cout << "condition is " << condition << std::endl;

    if(condition)
    {
        std::cout << "condition is true" << std::endl;
    }
    else
    {
        std::cout << "condition is false" << std::endl;
    }

    return 0;
}

You see, not all parts are executed in every program run. In no situation can both paths be traversed.

Flowchart

To make the sequence of operations in your program easier to see, the flowchart was developed. It helps you to represent processes graphically and describes the sequence of operations to solve your problem.

The symbolism of the flowchart consists of simple geometric figures. The most important ones are:

  • Rectangle with round corners: Terminator
  • Rectangle: operation
  • Rhombus: branching
  • Parallelogram: input and output
  • Line, arrow: connection to the next element

Each process starts and ends with a terminator. Rectangles stand for operations that are to be executed. Branches are indicated with a hash. In it stands the condition, which is to be fulfilled. There arise several alternative paths, which the program can continue to run through. Parallelograms are for input and output. All elements are connected either with a line or an arrow.

Of course there is also a standard for these diagram types, the DIN 66001.

Figure1 shows the flowchart for listing1.

Figure1: Flowchart

Figure1: Flowchart

Today, flowcharts are used less frequently. Programmers prefer to use pseudocode because it gives them a similar level of abstraction, while being faster to create and much easier to modify.

In my opinion, this only applies to people who know how to handle code. If you want to talk about the flow with someone who has little programming experience, then flowcharts are a good choice.

Nested if

There are situations in which several conditions must be fulfilled. This results in different constellations of true and false, to which different reactions may be required.

In this case you can nest if queries inside each other.

// listing2: nested if

#include <iostream>
#include <stdlib.h>

int main()
{
    bool condition1 = rand() % 2;
    std::cout << "condition is " << condition1 << std::endl;
    
    bool condition2 = rand() % 2;
    std::cout << "condition is " << condition2 << std::endl;
    
    if(condition1)
    {
        if(condition2)
        {
            std::cout << "condition1 and condition2 is true" << std::endl;
        }
        else
        {
            std::cout << "only condition1 is true" << std::endl;
        }
    }
    else
    {
        if(condition2)
        {
            std::cout << "only condition2 is true" << std::endl;
        }
        else
        {
            std::cout << "no condition is true" << std::endl;
        }
    }
    
    return 0;
}

I admit listing2 is not a very creative example. But I think you can see there well how nested if constructs work.

It is not necessary to indent the inner statements. The compiler doesn’t care. But they increase the readability of your code immensely. It’s much easier to see where the parentheses start and end.

Switch case

Let’s say we want to react differently depending on the value of a variable. So we have a variable that should be checked for several conditions.

Now, of course, you can query all conditions with a grouped if else construct. But this is very cumbersome and makes your source text less readable.

In listing3 the variable dice imitates the behavior of a random dice roll. The number of dice is used to decide which output is sent to the console.

// listing3: grouped if else

#include <iostream>
#include <stdlib.h>

int main()
{
    int dice = rand() % 6 + 1;

    if(dice == 1)
    {
        std::cout << "the dice counts one" << std::endl;
    }
    else if(dice == 2)
    {
        std::cout << "the dice counts two" << std::endl;
    }
    else if(dice == 3)
    {
        std::cout << "the dice counts three" << std::endl;
    }
    else if(dice == 4)
    {
        std::cout << "the dice counts four" << std::endl;
    }
    else if(dice == 5)
    {
        std::cout << "the dice counts five" << std::endl;
    }
    else
    {
        std::cout << "the dice counts six" << std::endl;
    }
    
    return 0;
}

But C++ knows a better way for this task: the switch case construct.

The different outputs depend here on an integer. With the keyword switch the value of an integer variable is queried. The different statement options are introduced with the keyword case and numbered consecutively.

The value of the integer variable specifies the case to be executed. The compiler then jumps to this position in the code.

You do not have to explicitly terminate a case. However, all following cases are also executed one after the other. You can prevent this with the keyword break. break causes the compiler from the switch construct to jump behind the closing parenthesis. From there on the program flow is continued.

With an example it will surely become clearer to you. We take again the cube from listing3. But this time with switch case.

switch evaluates the random number dice. Then we jump to the corresponding case and print the number of dice on the console. With break each case and thus the switch construct is closed.

// listing4: switch case

#include <iostream>
#include <stdlib.h>

int main()
{
    enum numbers                // enumeration for naming the switch cases
    {
        one,
        two,
        three,
        four,
        five,
        six
    };

    int dice = rand() % 6 + 1;  // dice throw, random number

    switch(dice)
    {
        case one:
            std::cout << "the dice counts one" << std::endl;
            break;
        case two:
            std::cout << "the dice counts two" << std::endl;
            break;
        case three:
            std::cout << "the dice counts three" << std::endl;
            break;
        case four:
            std::cout << "the dice counts four" << std::endl;
            break;
        case five:
            std::cout << "the dice counts five" << std::endl;
            break;
        case six:
            std::cout << "the dice counts six" << std::endl;
            break;
        default:
            std::cout << "Tell me the default state of a dice" << std::endl;
    }

    return 0;
}

Since switch() can only evaluate integers, Enumeration can be used to give the cases meaningful names. This increases the readability of your code immensely.

Conditions with Conditional Operator (?:)

C++ knows another and very powerful operator for evaluating conditions. With the Conditional Operator ? : you can replace an if else construct in a compact form.

The Conditional Operator consists of two parts. First you formulate your condition. To keep the overview, it is better to put it in brackets. Then the first part of the operator ? follows and shows the end of the condition.

After that comes the result that should be used if the condition is true. The second part of the operator : separates true from false. It is followed by the result if the condition evaluates to false.

In listing5 you can see how a conditional operator looks like in the source code and if I could describe it accurately.

// listing5: conditional operator

#include <iostream>

int main()
{
    int val1 {0};
    int val2 {0};

    std::cout << "Please, enter a number: " << std::endl;
    std::cin >> val1;

    std::cout << "Please, enter a second number: " << std::endl;
    std::cin >> val2;

    int greatVal = (val1 > val2)? val1 : val2;      // conditional operator

    std::cout << greatVal << " is the greater of both numbers" << std::endl;

    return 0;
}

Use the conditional operator rather for simple conditions. If it becomes more complicated, rather decide for if and else.

Like many things, the use of this operator is a matter of taste. Some programmers like its slim form. Others do not like it.

That sticks

We are now able to formulate conditions, and can make the program flow respond to them differently using conditional statements. You can use the if keyword to evaluate your conditions and associate the fulfillment with a statement.

In case the condition was not fulfilled, you can use else to create an alternative program flow with other statements. So if the evaluation of your condition returns true, the program follows the if path. Otherwise it follows the else path.

if else constructs can also be nested. However, if you have a condition whose evaluation can result in more than true or false, then nesting is not your only tool.

C++ offers you with switch case constructs the possibility to react more easily to different values of the condition.

With the conditional operator ? : we now even know a very short notation for simple conditions.

The presented constructs, keywords and operators let you design your program flow very well. With this you are able to react differently to queries and evaluated conditions.

Another concept with which you can control the flow of your program are loops. But more about that next time.

I wish you maximum success!


Sources

  • [1] B. Stroustrup, A Tour of C++. Pearson Education, 2. Auflage, 29. Juni 2018.
  • [2] B. Stroustrup, Programming: Principles and Practice Using C++. Addison Wesley, 2. Auflage, 15. Mai 2014.
  • [3] U. Breymann, Der C++ Programmierer. C++ lernen – professionell anwenden – Lösungen nutzen. Aktuell zu C++17. München: Carl Hanser Verlag GmbH & Co. KG; 5. Auflage, 6. November 2017