Hello and welcome. We have reached the end of the series of articles “C++: The Basics”. You can pat yourself on the back with pride. Not many have the stamina and motivation to go this far. This is also somewhat due to the nature of the subject.

Software programming in C++ is a huge subject area with a lot of content, some of it very abstract and complicated. Can be overwhelming at first and admittedly a bit dry at times. But the foundation has been laid.

In conclusion, let’s get all the basics together.

After getting a little overview of C++ and its history, we went right ahead and started in a very classic way with a Hello World.

// listing1: hello world

#include <iostream>

int main()
{
    std::cout << "Hello World!" << std::endl;
    return 0;
}

Without understanding much of the written code, we focused first on compiling and running the tiny application.

# listing2: compile and run

$ g++ -o hello hello.cpp
$ ./hello
# Output

Hello World!

Now we were able to see what our program did, and then wanted to understand how it was structured.

That’s when we first encountered preprocessor instructions and the main function, the body of your program. Along with that came the return value of the function and the namespace of the C++ Standard Library.

After that we started with primitive datatypes, custom enums and also Structures to explore the behavior of Variables and Constants. Built first small command line programs with input and output from this.

// listing3: input and ouput

#include <iostream>

int main()
{
  struct {
    char* initials{new char[3]};
    float floatNumber;
    int intNumber;
  } inputStorage;

  std::cout << "Hi, what are your initials?" << std::endl;
  std::cin >> inputStorage.initials;

  std::cout << "And now, please tell me a number between 2.0 and 3.0" << std::endl;
  std::cin >> inputStorage.floatNumber;

  inputStorage.intNumber = inputStorage.floatNumber;

  std::cout << "Hey " << inputStorage.initials
            << ", be careful with the data types, otherwise your " 
            << inputStorage.floatNumber << " will quickly turn into a"
            << inputStorage.intNumber << "!" << std::endl;

  return 0;
}
# Output

Hi, what are your initials?
ec
And now, please tell me a number between 2.0 and 3.0
2.4
Hey ec, be careful with the data types, otherwise your 2.4 will quickly turn into a 2!

But that was not enough and we took on more complex data types based on Arrays. From the C++ Standard Library, vector and strings followed.

Armed with a good selection of data types, we started relating variables with expressions and statements. To do this, we used a large number of arithmetic, relational, logical, bit and composite operators.

Operator Table

Figure 1: Operators overview

From then on, we were able to express ourselves, design variables and connect them to each other as we pleased. Or the use case demanded.

But we still lacked control over the programflow. So far, all of our instructions have been executed sequentially, one after the other, from top to bottom. However, this is not desired in every case.

Depending on certain circumstances, the program should also be able to take a different path. For this purpose, we have learned about selection and branching statements. With conditional expressions and if ... else or switch() statements we have let the program decide which part of the code will be executed next.

// listing4: 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;
}
# Output

condition is 1
condition is true

In addition, we used loops to make program code sections repeat for a specified number.

// listing5: for loop

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

int main()
{
    int guessable {rand() % 10};
    int guess {0};
    int numberOfGuesses {3};
    
    std::cout << "Guess a number between 0 and 10 " << std::endl;

    for(int i = 0; i < numberOfGuesses; i++)                // for loop
    {
        std::cout << numberOfGuesses - i << " guess left" << std::endl;
        std::cout << "Your guess: ";
        std::cin >> guess;

        if(guessable > guess)
        {
            std::cout << "I'm afraid you missed! Your number is too small. " << std::endl;
        }
        else if(guessable < guess)
        {
            std::cout << "I'm afraid you missed! Your number is too large. " << std::endl;
        }
        else
        {
            std::cout << "Great! "<< guess << " is right." << std::endl;
            break;
        }
    }

    std::cout << "See you!" << std::endl;

    return 0;
}
# Output

Guess a number between 0 and 10 
3 guess left
Your guess: 1
I'm afraid you missed! Your number is too small. 
2 guess left
Your guess: 2
I'm afraid you missed! Your number is too small. 
1 guess left
Your guess: 3
Great! 3 is right.
See you!

Now that we had the flow under control, we extended our capabilities with defining tasks as encapsulated functions. This powerful tool gives us the ability to structure our code in a meaningful way into small packages that we can reuse at any time.

// listing6: functions

#include <iostream>

const double Pi = 3.14159265;

// functions
double diameter(double radius)
{
    return 2 * radius;
}

double area(double radius)
{
    return Pi * radius * radius;
}

double circumference(double radius)
{
    return 2 * Pi * radius;
};

// main function
int main()
{
    double radius = 0.0;
    
    std::cout << "Enter radius: ";
    std::cin >> radius;
    
    // call function "diameter"
    std::cout << "diameter is: " << diameter(radius) << std::endl;
    
    // call function "area"
    std::cout << "area is: " << area(radius) << std::endl;
    
    // call function "circumference"
    std::cout << "circumference is: " << circumference(radius) << std::endl;
    
    return 0;
}
# Output

Enter radius: 3
diameter is: 6
area is: 28.2743
circumference is: 18.8496

We created prototypes, definitions and function calls. This increased the readability and clarity of our code.

We then went deeper into computer science and looked a bit at system memory. This helped us to better understand pointers and references.

With the two helpers, we have been able to control the amount of memory used by variables and how certain variables are accessed in memory itself.

// listing7: pointers and references

#include <iostream>

int main()
{
    int anInt{15};          // variable initialization
    int& refToInt = anInt;  // reference intialization
    int* pointToInt;        // pointer declaration
    pointToInt = &anInt;    // pointer assignment

    std::cout << "Value of variable = " << anInt << std::endl;
    std::cout << "Address of variable = " << &anInt << std::endl;

    std::cout << "Value of reference = " << refToInt << std::endl;
    std::cout << "Address of reference = " << &refToInt << std::endl;
    
    std::cout << "Value of pointer = " << *pointToInt << std::endl;
    std::cout << "Address of pointer = " << pointToInt << std::endl;
}
# Output

Value of variable = 15
Address of variable = 0x7ffccb621f34
Value of reference = 15
Address of reference = 0x7ffccb621f34
Value of pointer = 15
Address of pointer = 0x7ffccb621f34

From then on, it became very important to know the Scope of variables. From where are values to be reached? Are they the real values or just copies? How does the visibility behave? This is what we know now.

Outlook

Looking back once again at everything you have learned and consciously seeing your own development feels very good. This gives you a good basis and you can confidently move on to more advanced topics in the C++ programming language. And there is a lot to discover.

Object-Oriented Programming (OOP)

Have you heard of programming paradigms? These describe a basic programming style that a programming language is designed to follow. Even though C++ supports multiple paradigms, it is usually known as object-oriented. In object-oriented programming, the design is focused on data and software objects rather than functions or logic. The focus on objects is well suited for large and complex programs that are constantly updated or evolving. Task-specific encapsulated software units are created, which can be wonderfully reused. In addition it brings the advantages, if several developers work on a project. This is because a change to one object does not change the entire code and fewer conflicts arise.

Clean Code

When working collaboratively, it helps your team members if you pay attention to cleanliness, readability as well as structure when designing and implementing your code. There is a very popular book [4] about principles to write intuitive and easy to change clean code. There you will learn a fantastic number of acronyms, like KISS - Keep It Simple and Stupid, DRY - Don’t Repeat Yourself, YAGNI - You aren’t gonna need it, SOLID - Single-Responsibility; Open-Closed; Liskovian Substitution; Interface-Segregation; Dependency-Inversion. This way you make it easier for every programmer to understand and work with your code.

Design Patterns

You can also look at solution templates for recurring design problems. The so-called Design Patterns are listed and described in a book [5] that has not lost its importance to this day. The patterns can also be combined for your software architecture. So you can quickly use proven approaches for your code. They are really just patterns that you have to adapt accordingly for your use case. So don’t assume off-the-shelf solutions that are just to copy.

Advanced C++

If you don’t want to just advance yourself with new concepts, patterns, or principles, there are plenty of technical topics to learn as well. We have already looked at the C++ Standard Library, but so far only a very small part. You can go much deeper there. There is also new functionality in modern C++, such as Smart Pointer, Lambdas or Templates. You can also make your code more stable and secure by looking into Exception Handling. Or increase performance with multithreading.

That sticks

As you can see, our journey into the world of the C++ programming language has just begun. We’ve made it up the first few inclines, but there’s still a lot waiting for us to discover. In conclusion, all that remains for me to say is:

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
  • [4] R. C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship. Prentice Hall, 1. Auflage, 1. August 2008
  • [5] E. Gamma, R. Helm, R. Johnson, J. Vlissides, Design Patterns. Elements of Reusable Object-Oriented Software. Prentice Hall, 1. Auflage, 1. Juli 1997