Understanding Move Semantics and Perfect Forwarding: Part 3

Programming

street-1435744_1920

Perfect Forwarding and Everything Else

In the previous article I introduced rvalue references and discussed how they can be used to improve performance of C++ applications using move semantics. In this final article we will look at how rvalue references improve the flexibility of C++ code when writing template programs using perfect forwarding.

What is Perfect Forwarding

Perfect forwarding allows a template function that accepts a set of arguments to forward these arguments to another function whilst retaining the lvalue or rvalue nature of the original function arguments.

Perfect forwarding reduces excessive copying and simplifies code by reducing the need to write overloads to handle lvalues and rvalues separately.

Note: The function the arguments are forwarded to can be a normal function, another template function, or a constructor.

template<typename T>
void OuterFunction(T& param)
{
InnerFunction(param);
}

Without having written template code in C++ before this might not make a whole lot of sense so above is an example of a template function that forwards its argument param to another inner function.

Notice that the outer function accepts an lvalue reference, therefore we can only pass into it lvalues, the following code will not compile.

OuterFunction(5); // Wont work trying to pass an rvalue to lvalue reference

To fix this we could make the outer function accept a const lvalue reference to allow us to pass rvalues but then the inner function would not be allowed to modify its argument. Instead we would have to write an overload of the outer function that handles rvalues.

template<typename T>
void OuterFunction(T&& param)
{
InnerFunction(param);
}

More importantly what if our inner function must accept an rvalue reference as an argument. From the previous article we know that an rvalue reference is in fact treated as an lvalue so even if our outer function accepts an rvalue reference when it comes to passing that argument to the inner function it will be seen by the compiler to be an lvalue and is therefore not allowed.

The problems mentioned above plus many other issues that can crop up when writing template code is what perfect forwarding solves. However, to truly understand perfect forwarding several other concepts must first be understood.

Template Type Deduction

void OuterFunction(T&& t) {
}
…
OuterFunction(x);
OuterFunction(X());

Template type deduction relates to how the compiler deduces the type T passed into the template function when the functions parameter is of type T&&. Two rules govern how T is resolved that depends on whether the argument passed is an lvalue or rvalue. The rules are as followed:

  1. If the argument passed to T is a lvalue, then T is deduced to a lvalue reference, X&
  2. If the argument passed to T is a rvalue, then T is deduced to a lvalue X.

Basically, we can call a template function without having to specify the type T being passed and have the compiler determine the type itself with the rules above governing what the type T value becomes in respect to its expression value. The type of T is deduced from the argument passed into the function call.


OuterFunction(6);

If the above call was made the compiler would determine the type T as an int and given that an rvalue is being passed the expression value of T would be an lvalue,  T = int.


OuterFunction(helloWorldString);

If the above call was made the compiler would determine the type T as a string and given that an lvalue is being passed the expression value of T would be an lvalue reference. T = string&.

Note: type deduction occurs when the type is not known and therefore must be deduced, so in the case of OuterFunction(“Hello”); would not compile as the function excepts an lvalue reference of type string which an rvalue “Hello” cannot be bound to.

Reference Collapsing

Reference collapsing is a set of rules in C++ 11 to determine the value of T of a template function argument when trying to take the reference of a reference which is something that is illegal in C++. Taking the address of an address doesn’t make any sense but it can sometimes occur when writing templates.

template<typename T>
void func(T t) {
T& k = t;
}
...
string hello = “Hello”;
func(hello);

In the above example the type T is deduced to int& and then we are trying to store a reference to t in the form of k which means we are trying to do (int&)& k = t. Trying to take a reference to our reference.

Therefore, reference collapsing rules states what the value of T is dependent on the type of referencing that is occurring:

  • Taking the reference of an lvalue reference results in an lvalue reference X& & becomes X&
  • Taking the rvalue reference of an lvalue reference is an lvalue reference X& && becomes X&
  • Taking the lvalue reference of an rvalue reference is an lvalue reference X&& & becomes X&
  • Taking the rvalue reference of an rvalue reference is an rvalue reference X&& && becomes X&&

In any situation where an lvalue reference is involved the compiler will always collapse the type to an lvalue reference, if an rvalue references is involved then the type deduced is an rvalue reference.

Forwarding with forward

The function std::forward is required for solving the perfect forwarding problem with the functions purpose to resolve that awkward rule in which rvalue references are treated as lvalues (see the last article for more information).

The problem this causes is that even when passing an rvalue to a function that accepts an rvalue reference given that the rvalue reference parameter is treated as an lvalue reference we cannot then forward this to a function that accepts an rvalue. We have lost all the benefits move semantics gives us.

This is where std::forward comes in as it does two things dependent upon the value that is passed to the function:

  1. If passed an argument that isn’t an lvalue reference e.g. int& val, then it will return an rvalue reference.
  2. If an argument passed in an lvalue reference the function returns an lvalue reference, it does nothing to the argument.

This means that passing in an rvalue to a template function that accepts an rvalue reference will be able to forward that argument as an rvalue to any inner functions and if an lvalue is passed and we have an overload for our inner function that accepts lvalues then that function is called instead.

Universal References

The differences between a universal reference and an rvalue reference is that although it is denoted using ‘&&’ it doesn’t have to be an rvalue reference, it can sometimes mean ‘&’, be an lvalue reference.

As will be shown later this is how we can pass lvalues and rvalues to a template with a parameter of type T&& without the compiler complaining. The previous article referred to how a function with an rvalue reference can only accept rvalue references and that is true because in the context of a normal function ‘&&’ means rvalue reference. With template functions T&& can mean T&& or it can mean T&.

In general, ‘&&’ means a universal reference only when template type deduction is involved which is not the case if we are using a non-template function. With the advent of a new reference type there is unfortunately more rules that need to be learned:

  • If the expression initializing a universal reference is an lvalue, the universal reference becomes an lvalue reference.
  • If the expression initializing the universal reference is an rvalue, the universal reference becomes an rvalue reference.

Bringing it all Together, Perfect Forwarding

Bringing all the above rules together brings us what is referred to as perfect forwarding. A set of rules and functions within C++ that provide us with the ability to pass a value regardless of whether it is an lvalue or an rvalue and have that value preserved when being passed to a function called within a template function.

Below is a simple example of perfect forwarding where an object of type A has its constructor called within a template function, with the move constructor, or copy constructor being called dependent on the value passed to template function.


class A
{
public:
A(std::string b) : b(b) {}

// Copy Constructor
A(const A& other) : b(b)
{
b = other.b;
std::cout << "Copy Constructor" << std::endl;
}

// Move Constructor
A(A&& other)
{
b = std::move(other.b); std::cout << "Move Constructor" << std::endl;
}
private:
std::string b;
};
//And a template function
…
template<typename T>
void OuterFunction(T&& param)
{
A a(std::forward(param));
}
…
// Passing an lvalue
A a = A("Hello");
OuterFunction(a);

// Passing an rvalue
OuterFunction(A("World"));

After researching around on the internet I couldn’t find an answer that definitely explained how std::forward works but this post explains std::forward in the context of perfect forwarding.  Basically, std::forward returns a static_cast(t) when T is explicitly defined and t is our passed parameter.


template<typename T>
void func(T&& t) {
std::forward(t);
}

If we let the template parameter be defined as a universal reference T&& then std::forward is either going to be passed an lvalue reference or an rvalue reference. Given std::forward is basically a static_cast(param) where T is explicitly defined not deduced then reference collapsing rules determine the returning value where static cast looks either like static_cast<A& &&>(param) or static_cast<A&& &&>(param).

Passing an Lvalue

Looking at the above example we can look at how everything we discussed comes into practice. Passing the variable ‘a’ to OuterFunction which is an lvalue means that the type T is deduced to A&.

Next, given that the param now looks like A& & as universal reference rule states that if an lvalue is passed T&& is treated as an lvalue reference.  For param we can look at the reference collapsing rules to determine that param A&, is an lvalue reference.

Finally, param is passed to std::forward and the static cast of std::forward returns an lvalue reference due to the reference collapsing rules.

In this instance an lvalue reference is passed to ‘a’ and thus the copy constructor is invoked.

Passing an Rvalue

Passing the object A(“World”) an rvalue expression to OuterFunction results in T being deduced to A.

Next, given that param now looks like A && where the universal reference is treated as a rvalue reference, so we just get A&&.

Finally, param is passed to std::forward and the static cast of std::forward returns an rvalue reference due to the reference collapsing rules.

In this instance an rvalue reference is passed to ‘a’ and thus the move constructor is invoked and we can take advantage of move semantics avoiding an unnecessary copy of member variable ‘b’.

Step by Step Guide

To summarise, for perfect forwarding to work it requires that a template function accept a parameter of type T&& and that template type deduction is used. As well as this std::forward must have its type explicitly defined.

  1. The argument passed to a template function first has its type deduced, which results in an lvalue or an lvalue reference. T becomes T& or T
  1. Because a universal reference is being used if the expression passed to the function (the argument) is an lvalue then T&& param becomes T& param, if its an rvalue it becomes T&& param referring to an rvalue reference.
  2. At this point we have either (T& && param) or T && param and reference collapsing rules are applied so the parameter to the function results in either an lvalue reference or an rvalue reference.
  3. Finally, given that an rvalue reference is in fact treated like an lvalue reference std::forward is required to cast the template parameter to the correct value. Again, reference collapsing rules are required that determines if we have an lvalue reference then an lvalue reference should be forwarded and if we have an rvalue reference our value should be cast to an rvalue reference.

Summary

Admittedly when I first started writing this article I didn’t quite realise how complicated perfect forwarding was and how many subtle behaviours of C++ was required to fully comprehend what was going on. After all I still don’t fully understand std::forward and I can’t guarantee that what I have wrote is 100% correct but I think it makes sense. Either way  I hope this article and the previous two have provided enough information to emphasise the need and practical uses of rvalue references, move semantics and perfect forwarding within C++ 11.

Further Resources

https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers

https://eli.thegreenplace.net/2014/perfect-forwarding-and-universal-references-in-c

https://www.justsoftwaresolutions.co.uk/cplusplus/rvalue_references_and_perfect_forwarding.html

Understanding Move Semantics and Perfect Forwarding: Part 2

Programming

coding-computer-hacker-97077

Rvalue References and Move Semantics

In the previous article I discussed lvalues and rvalues, what they mean and how they can affect a developer’s ability to write C++ applications. In this article I will take it a step further and finally introduce rvalue references, and how knowledge of rvalues are required for implementing move semantics.

We looked at how passing arguments by reference was important for performance, yet this imposed limitations. Only lvalues could be passed by non-const reference, and rvalues could be passed if the reference was const, meaning that the value couldn’t be modified.

As C++ 11 introduces the concept of rvalue references, the references we looked at previously are commonly referred to as lvalue references. An lvalue reference can be assigned a modifiable (const) lvalue or an unmodifiable value, and if the lvalue reference is defined as const (unmodifiable) they can be assigned an rvalue.

An rvalue reference is a reference that is only assigned an rvalue, meaning you cannot pass an lvalue to a function that has an rvalue reference argument. Below is an example of an rvalue reference, denoted with a double ampersand ‘&&’.

int &&rvalueRef = (x+1); // rvalueRef is rvalue reference

Having the ability to use rvalue references in C++ 11 opens up the ability to use rvalues in ways that were not previously possible as shown in the following code:

string Hello()
{
    Return “Hello World”;
}
string &&rvalRef = Hello();
rvalRef = “Hello”;
std::cout << rvalRef << std::endl; // Outputs “Hello”

The above code highlights how rvalue references change the way we interact with rvalues. Firstly, rvalues are no longer restricted to live only within the expression they are defined.  An rvalue’s lifetime becomes linked to the rvalue reference, and will therefore exist until that reference expires.

Prior to C++ 11 we could extend an rvalues lifetime by passing it to an un-modifable lvalue reference, however in this case the rvalue was un-modifable. With rvalue references, the rvalue can now be modified.

Now we are finally here, we understand rvalues, lvalues, lvalue references, and rvalue references. We are almost at the point of learning about move semantics, but first we need to make sure we have an understanding of temporary objects otherwise move semantics are not going to make much sense.

Temporary Objects

A temporary object is an object created during the creation of an expression and its purpose is to provide a temporary location for the result of that expression. Without temporary variables we would have to define variables to store these results. This was covered in more detail in the previous article.

It is worth remembering that an expression in C++ is any piece of code that returns a value, so there are many times within a C++ application that temporary objects are created, some examples include:

  • Returning a value from a function
  • Literals
  • Implicit conversions
  • Passing by value to a function

The reason why we are talking about temporary objects is because they are rvalue expressions, and whenever a temporary object is created, memory is allocated for that object, and the result from an expression is copied into it. The act of copying data into temporary objects is what can cause seemingly hidden performance issues within a C++ application, and are what move semantics aim to address.

Move Semantics

Move semantics aim to avoid the copying of data from temporary objects by instead stealing the memory location of where the object resides. This behaviour is implemented through the use of a move constructor and move assignment operator that act only on rvalue references.

MyClass(MyClass&& other)
{
_buffer = other._buffer;
_length = other._length;

other._buffer = nullptr;
other._length = 0;
}

In the above move constructor the following things occur. An rvalue reference argument is defined, so that the function only accepts rvalues like temporary objects. Given that _buffer is a raw pointer to an array, the _buffer pointer is simply assigned to point at the location of our temporary object and _length is updated to store the _length of other._buffer.

Finally, the rvalue reference other is reset to its default value. Assigning other._buffer to nullptr ensures that our temporay object no longer points to this data. This is important as we previously mentioned the lifetime of the temporary object is linked to the life time of the rvalue reference, which expires at the end of the move constructor. When the temporary object is being destroyed, like any other object its destructor will be called which would free up all the memory it was allocated i.e. our _buffer array.

The result of this move constructor is that we have now copied our object without having to copy any data, we have essentially just stolen the address of the temporary object instead.  For reference, below shows how a move assignment operator would look.

MyClass& MyClass::operator=(MyClass&& other)
{
    if(&other != *this)
{
delete[] _buffer;
_buffer = other._buffer;
_length = other._length;

other._buffer = nullptr;
other._length = 0;
}

A move constructor and move assignment operator can only be passed rvalues, whenever an lvalue is used, the copy constructor, or copy assignment operator will be called which means data will simply be copied to the new instance of the object. If we want to treat an lvalue like an rvalue so we can make use of move semantics then we need to cast an lvalue to rvalue.

std::move

In addition to the things already discussed, C++ 11 introduces a method std::move, that allows casting an lvalue to an rvalue. The method does not move the data, that is done by the move constructor, but by converting the lvalue to the rvalue it allows move constructor to be called.

std::string hello = “Hello World”; // lvalue

std::string(hello); // hello an lvalue so copy constructor called

std::string(std::move(hello)); // cast hello to an rvalue allowing move constructor to be called

Caution must be taken when using std::move. When working with a temporary object if we pass it to a move constructor we know that the temporary object (the rvalue reference) is going to cease to exist once method has finished being executed. With an lvalue, its lifetime might not be so short and by using std::move and essentially treating it like a temporary object we could end up deleting data that is used somewhere else in the program.

Therefore, it is wise to only use std::move on lvalues that we know aren’t going to get used anywhere else in the code, such as if they are local to the function in which a move constructor is being called.

Rvalue References are Lvalues

We now know that if we want our move constructor or move assignment operator to be called we must ensure we are passing an rvalue or first casting lvalue to an rvalue with std::move. It might not be apparently obvious but we can still end up calling the copy constructor if we are not careful.

MyClass(MyClass&& other)
{
_buffer = other._buffer;
_length = other._length;
_name   = other._name;

other._buffer = nullptr;
other._length = 0;
other._name = "";
}

In the above code we have modified the class MyClass to store a string object and updated our move constructor to accommodate this change. At first it might seem that when we execute _name = other._name we are calling the move constructor of string when in fact we are actually calling its copy constructor.

The copy constructor is being called because when an rvalue is passed to an rvalue reference this value is in fact an lvalue. This makes sense given the definition of an lvalue. A value that has an identifiable location in memory which you can get the address of. If the rvalue reference is in fact an lvalue then the objects copy constructor will be called because we are passing it an lvalue. To ensure the move constructor is called we must use std::move, this is safe because the original object being passed in should be a temporary object or an lvalue we know has limited lifetime.

MyClass(MyClass&& other)
{
_buffer = other._buffer;
_length = other._length;

// None primitive types will have their copy constructors called unless we first cast them to an rvalue.
_name = std::move(other._name);

other._buffer = nullptr;
other._length = 0;
other._name = "";
}

Summary

Hopefully you now have a good understanding of rvalue references, move semantics, and how move semantics are useful for improving the performance of C++ applications by avoiding the need to copy data when we temporary objects are involved.

Fortunately for us, move semantics are supported in modern C++, meaning the standard library has been updated to include move constructors and move assignment operators for all objects and can support any user defined objects that contain their own.

In addition, move constructors and move assignment operators will be implicitly created unless the user defines: a copy constructor, copy assignment operator, or destructor. So as long as we are not handling resources with raw pointers, or requiring deep copies of objects that rely on a custom copy constructor, we can happily reap the benefits of move semantics without making changes to our code.

 

Understanding Move Semantics and Perfect Forwarding: Part 1

Programming

agriculture-arrows-asphalt-977603

An Introduction to Rvalues and Lvalues

With the introduction of C++ 11 comes the ability to further improve the performance and flexibility of C++ applications thanks to the introduction of move semantics and perfect forwarding. Two concepts that rely on rvalue references.

Before discussing these new concepts an understanding of rvalues and lvalues is required. These concepts are themselves not too difficult, but the evolving nature of C++ and the changes in definition of these values as the language has matured can make them rather confusing for new developers.

At the beginning rvalues and lvalues were defined in such a way that an rvalue very simply meant something that appeared on the right-hand side of an assignment, and an lvalue on the left. For example:


int num = 3;

4 = num; // 4 is a literal which is an rvalue and num is an lvalue thus the following code isn’t valid

But something like the following is acceptable:


int num = 4; // num is an lvalue and 4 is an rvalue

As time moved on and the C++ language evolved, so too did the definitions of rvalues and lvalues. Unfortunately, their names did not evolve with it. In more modern versions of C++ rvalues and lvalues took on new meanings.

Lvalues and rvalues became something used to describe the results of an expression (An expression being any code that returned a value) and as such an expression would be described as either an lvalue expression or an rvalue expression.

To the compiler this defines amongst other things how the result of an expression is stored. To a developer, which I am assuming you are, it basically means that the lvalue has a name and an identifiable location in memory that can be accessed using ‘&’. An rvalue is something that does not have an identifiable location in memory and thus it cannot be accessed with ‘&’.

An easy way to identify whether an expression returns an rvalue or an lvalue is to determine if you can take address of the value. If you can its an lvalue otherwise it’s an rvalue.

Rvalues are temporary and have short lifetime where they exist within the expression they were created. Because of this C++ doesn’t allow access to them through the address of operator due to the amount of problems it could occur by trying to access the address of short lived data.

That about wraps it up for what rvalues and lvalues are, but I can assume that the above definition does little to explain their actual use in writing of C++ applications. I will attempt to amend this in the next section by explaining how they can cause issues when writing code.

As previously mentioned you cannot obtain the address of an rvalue because C++ won’t allow it. This means that when writing code there are a lot of values that we cannot have references to. Literals, functions that return by value, and temporary objects are just a few examples. This means that a reference cannot be stored to these values, but more importantly these values cannot be passed to functions that accept reference types.

Unfortunately, passing by reference is a common method of improving the performance of an application as it avoids the need to copy data between memory locations, which if there is a lot of data can take up too much time in our time critical applications.

Therefore, to avoid these issues we can take several steps. Simply try and avoid passing rvalues to functions which means the following code:

int sum(int& a, int& b) { return a + b;
Sum(3, 5);

Would have to be written as follows:

int sum(int& a, int& b) { return a + b;

int a = 5;
int b = 3;
int c = sum(a, b);

The convenience of using rvalues is lost to maintain performance but at the cost of readability and time. A second option is to pass by value but that could potentially mean hindering performance. A third option exists, and that is to pass by reference but making the parameter of a function const.

int sum(const int& a, const int& b) { return a + b;
Sum(3, 5);

C++ allows an rvalue to be assigned to a reference which is an lvalue if and only if that lvalue is defined as const which stops it from being manipulated. This means the rvalue is technically an lvalue within the scope of the function, and the function is now able to accept rvalues whilst still preserving pass by reference.

An obvious problem that arises is that if a function needs to manipulate a reference then it can’t without overloading that function with a non const variant when wanting to pass an lvalue. Rvalues bring up additional problems in C++ which weren’t addressed until C++ 11.

For instance, C++ has a tendency to overindulge when it comes to copying values and there are issues with forwarding values to functions called within template functions. These issues are combated with move semantics, and perfect forwarding which will be a lot easier to understand now that you have an understanding of rvalues and lvalues and will be the focus of the next couple of articles.

Tricky C++: Making Things Constant

Programming
stained-glass-spiral-circle-pattern-161154
C++ is a confusing language that is difficult to write and in some ways even more difficult to read, especially if a developer doesn’t follow good programming practices. One way of writing better C++ code is to make use of the const keyword. Const enforces the policy of un-modifiability and can be used in conjunction with both variables and class methods.

Const Variables

A constant variable is a variable that cannot be altered, and must be initialised when declared because it cannot be changed later on! The following is an example of a const variable.
1
​The syntax for a constant variable is similar to that of any other variable, bar the introduction of the const keyword that is added before defining the type of the variable.
Note: A constant reference acts like a const variable except the address stored in a reference points to data that cannot be modified as opposed to storing the unmodified data itself.

Const Pointers 

Any type in C++ can be defined as const, including pointers. In the case of a pointer the position of the const keyword has a different meaning. The code below shows different ways in which const is applied to a pointer.
 2

The above example shows the three different ways in which const can be applied. Applying const anywhere before the ‘*’ states the data located at the memory address stored by the pointer must be constant. In this case the pointer has to point to data that is defined as constant.

Applying const after the ‘*’ states that the pointer itself is const, i.e. the address stored in the pointer cannot be altered, which also means the pointer must be initialised when declared.

Note: A pointer with a constant address can be compared to a reference, as a reference cannot alter the address it stores after it has been initialised.

Const to non -const 

There is one final issue with regards to const that needs to be addressed, regarding the assignment of a const variable to a non const variable. A variable that is constant cannot be assigned to variable that isn’t defined as constant if the assignment copies the address of the value, and not the data itself.

Assigning data from one variable to another involves copying the data, and storing it in a new memory location. Because of this when the new variable modifies the data it is modifying its own copy and not the original data defined as constant, so the original data remains unchanged. Whereas passing the address of constant data to a variable that isn’t defined as constant would give that variable permission to modify the data, which was defined as unmodifiable. This would defeat the purpose of defining the original variable as constant.

Const Methods

Additional to variable constness, const can be applied at the end of a class method and will enforce that the object will not be altered in this function, i.e. the data members of the object cannot be changed.  There are few rules regarding the use of const:

  1. Const methods can be called by non const objects because defining a method as const simply states that member data won’t be changed. The object calling the data may still want to change its members in a different method.
  2. Defining an instance of an object as const, and therefore unmodifiable, means that only const methods can be called by that object. Again if the object is defined as constant then calling non constant methods doesn’t make sense as these methods have the ability to alter the data of the object.
  3. A constant function can only return a constant value.


Note:  const is included in a function signature which means two methods can be produced, one const, and one not dependent on the object calling the method.

Consts in Use

The use of const within C++ is a tool to make code easier to read, which in turn can help with maintainability. There are several different ways in which constant values can help achieve this.

The most common example of using const is to define a set of values as const which will be unchanged but used extensively through a program. If there is a case where the same value is used repeatedly throughout, it is better to establish that value as a constant and then use the variable throughout the code instead. The result of this is that fewer errors can be made because the value is being written only once, when the variable is initialised. Secondly if the const value does need to change this only needs to occur in one location. Finally, it is easier to read a program that contains constants, than simply values, because they are named, and because in some cases a programmer might use values that are not commonly known, and this can make it difficult to understand their purpose.

Note: consts variable names are usually made capital e.g. pi becomes PI, which makes it easier to identify these values within a program.

The use of constentness helps clarify the intended use of a variable or method. Defining a parameter or return type as constant means that the values being used in a function or returned by that function are not to be modified. A const method tells someone that this function was not created with the intention of altering the members of a class. Any lack of understanding that results in an attempt at altering a constant value will cause the compiler to complain, so a programmer has no way of mishandling the piece of code.

A final example is the use of const to help with encapsulation. To summarise, encapsulation refers to hiding data and methods from other classes and is used to ensure no unauthorised manipulation occurs to the class. A classes interface defines the behaviours the class carries out, and these behaviours establish how, if possible, the data a class stores can be manipulated. By not hiding data or certain methods in a class an outsider could cause issues such as assigning an unwanted value to a variable that could cause errors, for instance assigning a zero to a value that is used within a division.

The use of const can help enforce the concept of encapsulation, specifically when using Getter methods within a class. A value returned “by value” from a Getter does not break encapsulation as a copy of the data stored in the class is being manipulated and not the original. However, if an address is returned instead, then objects outside of the class have the ability to manipulate the data stored within the class, which does break encapsulation.

Summary

To summarise, this article has provided overview of the const keyword, how it can be applied to variables, and methods, as well as providing examples of how const can help create more readable C++ code.

  • using const on a variable means its data cannot be altered.
  • const pointers can mean the data cannot be altered, the address the pointer stores cannot change, or both.
  • A constant pointer or reference cannot be assigned to a non-constant value because that would allow the data stored at the address to be changed
  • Using const with class methods prevents that method from altering the data members of the class.
  • Const makes C++ code more readable and should be used as an alternative to literals if they are used extensively within a program and do not change.
  • Using const helps definition the intentions of a variable or method
  • Having constant return values within Getter methods helps enforce encapsulation by ensuring the returned value cannot be altered from outside the class.

Tricky C++: Making Memory Management Easier with Smart Pointers

Programming
black-and-white-code-programming-tech-79290
One of the problems with writing programs in C++ is that using dynamic memory can be troublesome as it requires manually managing the allocation and deallocation of the memory you want to use. Unfortunately, dynamic allocation is an important feature in C++ and is required when using virtual functions, for polymorphic behaviour. Dynamic allocation is also required if you intend to store data that is larger than the stack (over 1MB), or of an unknown size at compile time, for instance, data received as input from a user. There are other benefits such as allowing the creation of dynamic data structures, and allowing objects to exist beyond their original scope.  Also did I mention you have to use pointers when allocating memory dynamically.

The problems that occurs with dynamic allocation relates to the manual memory management as mentioned previously. Two important issues that can arise are dangling pointers and memory leaks. The former refers to an instance in which a pointer attempts to access memory that has previously been freed. The latter relates to forgetting to deallocate memory leaving it inaccessible and unusable during the execution of the program. If this is done repeatedly, the amount of memory available to other programs reduces, eventually causing instability and crashes of the system. Therefore, there is a significant risk involved in using dynamic memory, however it is sometimes unavoidable. With the release of C++ 11, came the introduction of smart pointers, a feature that makes it easier to handle dynamic memory.

Smart Pointers

​Smart pointers avoid manual memory management by automatically managing the memory for you. They can be a little more confusing to understand than raw pointers, mainly due to their being several different types: shared, weak, and unique pointers. The difference between these pointers is in the type of ownership semantic they implement. Remember that one way of helping to reduce memory management issues is to define who owns the object so that it is the owner, and just the owners responsibility to handle the creation and destruction of the object.

Types

A unique pointer (unique_ptr) is a smart pointer that has exclusive ownership semantics. What this means is that the resource can only be owned by the unique pointer, and when this pointer goes out of scope, the resource is released. The only way for a unique_ptr to “share” the resource is to transfer ownership of that resource using the move semantic.

A shared pointer (shared_ptr) uses the concept of shared ownership semantics. Multiple shared pointers can refer to a single object, when another shared pointer refers to a resource, a reference count is increased. Only when the last shared pointer goes out of scope, is the memory released.

weak pointer (weak_ptr) acts like a shared pointer in that it provides sharing semantics, however a weak pointer does not own the resource. Meaning that someone else, a shared pointer, must already own a resource that weak pointer can share. Furthermore, the number of weak pointers do not contribute to the reference count of a resource, therefore a resource will be released even if weak pointers still share that resource. This means that a weak pointer cannot access the object it shares. In order to do so a shared pointer must be created from this weak pointer.The reason this is done is so that the resource cannot be destroyed whilst being used by the weak pointer because there will be at least one shared pointer accessing the resource. This does not prevent a weak pointer from accessing a resource that no longer exists.

Usage

This all well and good though but you may be asking when should you use each pointer. Well like most of programming it depends. However understanding the ownership semantics of each type goes a long way.

​A unique pointer is meant solely for single ownership. For example, if you were to have an engine class that had the responsibility of creating and initialising the subsystems of an engine, like the renderer, or physics system. Then it is not unreasonable to assume that the Engine class would be the sole owner of each subsystem and would store each in a unique pointer. When the Engine class is destroyed which would typically be when the game using it is closed, then all the subsystems would be shut down, and the resources related to these systems freed when the instance of Engine goes out of scope. Although ownership remains with Engine, the subsystems can still be accessed by other parts of the game engine by referring to the pointer stored by the unique pointer, accessed through the get() function. This allows other objects access to the resource, the desired system.

A weak pointer doesn’t have ownership but has the ability to share a resource and can be constructed to share the resources of a shared pointer, not a unique pointer. Therefore, a weak pointer can be useful in situations involving shared pointers where you don’t want a function or class accessing the resources of the shared pointer to also control ownership of the resource. Going back to the idea of a game engine again. If we were to have a game object class that stored a list of all the components attached to it, suppose we wanted the components to communicate with other components in the same game object. One idea would be to allow the component to hold a reference to the its parent, the game object. This reference could be stored as weak pointer because it doesn’t make sense for the component to be able to destroy the game object, its parent The game object would most likely have ownership over the components, and these components would be destroyed before the game object.

​A shared pointer can be used in situations in which a unique or weak pointer are implemented except for when using them causes a cyclic dependency. This relates back to the second example with game objects and components. If we have a shared pointer in game object that stores a component, and a component stored in the game object holds a shared pointer to the game object, its parent as shown below.

screen-shot-2017-11-27-at-20-43-08.png
In the example shown above neither the spaceship or laserWeapon objects will be deleted. When both objects are created a reference count of 1 is set for both. When gameObject holds a reference to laserWeapon the reference count is incremented to 2, and the same occurs when laserWeapon holds a reference to spaceShip. When the program reaches the end of main both objects will go out of scope, and thus the reference count for both is reduced to 1 because we have lost one owner.  laserWeapon’s reference count won’t hit 0 until spaceShip’s reference count hits 0, and vice versa, creating a circular dependency that results in the memory of both never getting deleted. Neither reference count hits 0, so C++ doesn’t think that the memory has to be deallocated.

Tricky C++: An Introduction to Pointers

Programming

arrow-2766163_960_720

Introduction

Pointers are prevalent throughout C++ and it is important to understand them in order to take advantage of many of features that exist in C++. However, for a beginner, they can be rather tricky to grasp, especially given how confusing the syntax can be. This article attempts to alleviate the pain of learning pointers,  by explaining what they are, how to use them, and why we would use them.

What is a Pointer?

A pointer is a variable that stores the memory location instead of the data itself. The basic syntax for a pointer looks like the following:
pointer

The above syntax shows a typical example of how to declare a pointer. The address-of operator ‘&’ is used to obtain the address of the data stored in the variable a, and it is that address that is stored in ptr. Although our pointer stores a memory address it is still necessary to declare the type of data that the memory address stores, so that the pointer can be used to manipulate the data.

Note: The ‘*’ operator is used to declare that the variable is a pointer, the position of ‘*’ can be positioned int* pointer or int *pointer, however the former declaration can be slightly confusing as it mimics the syntax required to dereference (explained next) a pointer.

Dereferencing a Pointer 

As previously mentioned a pointer stores the memory location and not the data, therefore a pointer is pretty useless at using that data unless you can gain access to it. Dereferencing does exactly that, and provides a way for you to access the data stored in the memory location of a pointer. Dereferencing ptr looks like the following:
dereferenceoutput
 In this example we use the dereference operator ‘*’ (yes, it is the same operator used for declaring a pointer) to gain access to the data stored at the address it points to, in this case it is the value 12. The value is stored within a variable b and is then used in a simple expression.

Note: Care must be taken when more than one pointer holds the address to the same data. Altering the data with one pointer reference in one part of your code will affect the data being used by all the others, and this can cause undesired behaviour.

Object Pointer

So far pointers have been described using C++ fundamental types, however pointers can be used to store the address of an object. The syntax is just a little bit trickier when accessing member data or methods. The following example shows one way of using a pointer with an object of type Student.
student
The second statement dereferences the pointer containing the address of the object, and then uses the dot operator to access the member function GetMathMark(). Without the parenthesis, the statement would first try to access the method of the object and then attempt to dereference, due to the operator precedence defined in C++. Given that the pointer stores an address and not the type Student this would not work out well. The syntax can be rather troublesome, therefore there is an alternate way which uses the “->” operator, which allows pointer to member access, and removing the need for parenthesis.
student2

​Pointer to a Pointer 

Another aspect of pointers is that they are able to store the address of other pointers. Yes, pointers can store the address of a pointer, which the pointer of the address it stores points to the address of some data. An example is shown below using C++ fundamentals, but this also works on user-defined types.
pointer as pointeroutput2
In this case, pptr is initialised with the address of ptr not the address of the variable a, and a can be accessed by either dereferencing ptr using a single “*” or through pptr using two “*”. ​As you can see the syntax for a pointer to a pointer is similar to that of a normal pointer, with the exception of an additional “*” in both the pointer declaration and when dereferencing.

When a Pointer is Required

So now the basics of pointers has been explained, the next question is why do we need pointers? Pointers are used extensively in C++, and below are a few examples, with no emphasis on describing any particular use case, as each could be an article of their own.

  • Polymorphism
  • Dynamic allocation
  • Efficient parameter passing (Although it is better practice to use references)
  • Function pointers

​Therefore, as you can see pointers are required if you want to make the most of C++, and make use of certain features of the language such as polymorphism, which is an important concept in any object orientated language.

Summary

To summarise, this article has introduced pointers in C++ and has provided a basic understanding of what they are, how they are used, and what they are used for.

  • A pointer holds the address of a variable, not the data itself.
  • The data stored at the memory address the pointer stores can be accessed by dereferencing the pointer.
  • A pointer can be used to store the address of an object, and requires parenthesising the object being dereferenced before using the member access operator, or using the “->” operator
  • Pointers can store the address of other pointers and is signified by the number “*” used when declaring and dereferencing a pointer

An introduction to Theoretically Evaluating Algorithm Efficiency with Asymptotic Notation  

Programming

speedometer-653246_1920

Don’t get scared away by the title; what we are going to talk about isn’t all that complicated. In a previous post I introduced the concept of an algorithm, and gave a brief description into how an algorithm is deemed to be efficient. This article will take this further by discussing techniques available to us for testing the efficiency of our algorithms before we go about implementing them in code.

Before we get started, lets first go back over efficiency. An algorithm can be efficient if it meets the memory or running time requirements imposed. Basically, our algorithm must use less than a maximum amount of memory, or run no slower than an amount of time specified. The restrictions imposed are dependent up on the problem we are trying to solve.

In order to test for efficiency, an algorithm must go through a theoretical analysis, using asymptotic analysis, before the algorithm is implemented.

The reason for this theoretical analysis is that simply without it our algorithms could only be tested through implementation.

Why is this bad? Well, firstly, we have to perform the implementation before we have any idea of how the algorithm will run. Meaning you could spend a long time developing something to realise that this algorithm will not run the way you want it to.

Secondly, by testing an algorithm through implementation we are making our algorithm dependent upon a specific platform, programming language, and hardware configuration. Altering any of these variables could result in a different result. Given the shear amount of variation we could never test our algorithm for all possible configurations.

Having a way of analysing our algorithm before we start implementing it allows us to save time, but more importantly allows us to judge efficiency independent of any hardware or software.

As described by Wikipedia, asymptotic analysis is the field of mathematics for describing the limiting behaviour of functions. A limit of a function is the value a function approaches as the input of that function approaches some value, usually zero or infinity.

Therefore, we are looking at the output of our function against a specific value, based on the values we are passing into the function.

If we have the function f(x) = e^x we could look at the output of that function as x tends towards infinity. Basically our function output grows exponentially as the value of x gets larger.

Asymptotic notation is a language that describes the type of behaviour of a function respective to the growth of that function. What I mean by this is that given a function f(n) = 2n^2 + 600n + 200 we are only concerned with the most significant term, n^2, because as n tends towards infinity the other terms and constants become irrelevant, as shown in the graph below.

As you can see from the graph the n^2 term results in a significantly larger output as the input size increases.

There are a few different types of notation and in the next article we will go into a lot more detail about one of them, but for now lets talk about how all this relates back to algorithms.

This idea can be applied to our algorithms, whereby the input of our function is the size of our input of the algorithm. Input is the metric we use as algorithms are designed to work with inputted data because an algorithm is useless without it. A search algorithm requires elements in which to search, as does a sorting algorithm needs input to sort.

As the input increases in size we can see that an algorithm might take longer to complete, or require more memory. It would take a lot less CPU cycles, or steps to search through a 100 items as it would do to search through 100,000.

This leads us onto the output of our function, which is what we want to measure for within our algorithm. If we are measuring the time it takes to run then we would like to see how long our algorithm takes to complete as the input amount increases. If we want to measure against memory, we would want to see how much memory is used up as the amount of input increases.

Therefore asymptotic analysis is required to measure the running time or memory capacity required by our algorithms as the input size increases. Asymptotic notation is where we describe our function as a rate of growth using the most significant term, and removing any insignificant terms or constants. We end up with an independent method for determing the efficiency of an algorithm.

In the next article we will look at a specific form of asymptotic notation, Big O notation, which is commonly used in computer science for measuring an algorithms performance.

Programming Fundamentals: Algorithms

Programming

060417_1744_Programming1.jpg

Welcome to the final article in this series on programming fundamentals. Over the last several articles we have looked at many important concepts that are applicable to any programming language you will be using. These concepts included: variables, data structures, conditions, repetition, and functions. In this last article, we look at algorithms, something that requires the use of all the concepts previously discussed.

At the most basic level we can define an algorithm as a set of steps that when finished result in the completion of a task, or the solution to a problem. The first article in this series introduced an algorithm for making a cup of tea. Under this definition though we could easily deduce that entire programs are algorithms as they are made up of a series of steps, albeit many steps, for completing a task. However, when we discuss algorithms in the realm of computer science they are generally seen as small concise steps intended to complete a specific task.

Algorithms can be classified dependent upon how they go about solving a problem. Some examples of types of algorithms include: divide and conquer, greedy, and brute force algorithms. The classifications give details with regards to how the algorithm performs. A brute force algorithm is one that will try all workable solutions until a match is given. For example, if we wanted to find out a person’s pin number we would try to enter every 4-digit combination until we entered the correct one.

Over the years, a multitude of algorithms have been developed that have been applied to solve a wide range of problem from searching, and sorting data within a data structures, to rendering realistic graphics in games. In most cases, it is up to the developer to use an existing algorithm to solve a specific problem dependent upon the problem at hand. In some situations though, you may have to modify an existing algorithm to suit your need, or even design your own.

Algorithm design involves developing a series of steps that can be reused to solve a specific problem. There is a lot that goes into designing an algorithm. We must understand the problem we are trying to solve, ensure that our algorithm works for all the values we expect to be input, and that the algorithm is efficient. Efficiency generally refers to how much memory we need to use whilst our algorithm runs, and how long it takes for our algorithm to complete.

Algorithms are essential in computer science. They are designed to solve problems, but also to be reusable, so that they can be then applied by developers for whatever they need. A search algorithm could be used, for instance, to sort a range of numbers from highest to lowest in a leaderboard, We decide how to use them, and having so many algorithms already designed for us, we are not short of options.

So there we have it, a quick overview of algorithms. I purposely left this last article light on details as algorithms are such a broad topic which cannot easily be explained in this article alone. But at least you now have some understanding of what they are.

I hope this series has provided a brief introduction, so if you look elsewhere on your journey to becoming a programmer and run into the word algorithm, variable, data structure, or anything of the other things we have discussed then you will know exactly what is going on, and a little a bit about the why.

The last point to make is that this is unfortunately only the beginning. There are a lot of concepts I haven’t discussed, some big ones such as object orientated programming, recursion, nesting, scope, and many more things. But there are plenty of helpful people out there to guide you on your way. Good Luck, and have fun!

Programming Fundamentals: Functions

Programming

050417_1955_Programming1.jpg

In the previous post that can be read here, we looked at repetition, the process of telling a computer to repeatedly execute a set of instructions based on some condition. In this article, we will delve into functions, what they are, how we use them, and how best to design functions in our programs.

Yet again, before we delve into functions, there are some things we need to know first. Mostly we need to look at statements and compound statements.

In most programming languages, the smallest element we can use to create a program is known as a statement, which up until now we have been calling an instruction. We have already looked at several different statements: if-statements, while-statements, and for-statements, but other statements exist such as assignment statement, assigning values to our variables, and expression statements, which is a statement that produces a value, for instance 5 + 5 * 2.

Often it takes more than a single statement to get something done, and that’s where compound statements, also known as blocks, come into play. If-statements, while-statements, and for-statements are all examples of compound statements. That is, they comprise of more than a single statement. In most programming languages, we define a block of code using a set of curly braces, so an if statement would look like the following:

If(condition)

{
// statements in here

}

The above example shows an if statement, and curly braces with all instructions within the curly braces being the ones that are executed if the condition evaluates to true.

There are two main benefits for using code blocks. They allow us to define scope, something I won’t be touching on in this article, and that as you have already seen, they allow us to group a set of statements. One thing we can’t do with our compound statements is use it multiple times throughout our program, which finally brings us nicely onto functions.

Functions

A function is like a named compound statement which can be referenced by its name throughout our code, and thus used multiple times. However, unlike a compound statement, our functions have additionally properties. They can accept and return data in the form of variables or data structures.

A function needs a name so we can identify it, like a variable does so we can access the memory location our data resides in, a function name is an identifier to the address were our group of statements are stored. As a function can accept and return data, we also must define this when creating our function. A name, return type and list of accepted data forms the signature of a function. Below is an example of a function:

int AddNumbers(int a, int b)

{

    int c = a + b;

    return c;

}

In the example shown we have created a function called AddNumbers which accepts two variables, also called parameters, called a and b, defined within a set of parentheses, and we have our return type defined as an int, placed before the name of the function. The idea behind this function is that it accepts two integer numbers, and then we add these two numbers together within the function and return the result.

There are no restrictions on the type of data our function returns, it can be a primitive type, or user-defined. Additionally, we can pass in any number of variables of any type, in which they don’t have to be of the same time, we also don’t have to pass in any variables at all. In most languages, we are also allowed to return nothing, which is typically done by specifying the return type as void.

void PrintHello()

{

    Print(Hello);

}

For us to use the functions we create we must call them within the parts of our program in which we want to use them.

Calling the function, is done by using the name followed by a set of parenthesis, following on from the example above we would call the function AddNumbers in the following way: AddNumbers(5,3). The values we passed in are stored in the variables a and b respectively and are then added together, return the variable c which will equal 8. In the example just given though we are calling the function but we are not doing anything with the value returned. To make use of the return value c we need to store that data somewhere, like in a variable. To call the function and store the value would look like the following:

Int d = AddNumbers(5,3)

Functions can be called from anywhere in our program, that is we can all them within loops, if statements, or even within other functions. Functions essentially point to a block of instructions that we want to execute so when we call a function you can think that we are just adding that block of instructions into our program at that point.

As you can start to see functions are a powerful concept. They allow us to reuse a set of statements as many times as we want in our program reducing the amount of instructions we need to write. They also allow us to better organise our program, making it easier to maintain, well that is if we design them properly.

Designing Functions

When deciding whether to write a function there are a few things worth considering. The first step is to decide whether the instructions you want to put into a function are going to be used more than once, if not then you might not have to put them in one.

Secondly you must decide on the return type. Do you think your function should return anything, and if so, then what? Then finally we need to figure out what parameters if anything we need to pass into the function. The specifics are all dependent on the problem you are trying to solve, or the program you are trying to write.

If we wanted to do any mathematical operations such as adding or subtracting numbers then we can assume that we would want to pass in the numbers we want to add together either as variables or as a data structure. We would also probably want to use the result of the function, and therefore should return it.

If we wanted to output something to the screen and wanted to write that into a function we would mostly likely have a parameter of the thing we want to print: a number, or word, but we would most likely not want to return anything as we simply want to output to the screen.

I think the most important thing to think of when designing functions is to remember that a function should only do one thing. If we want to write a program that adds two numbers together and then prints them out we can see that the there are two things we want to do, add numbers, and print them, and that these tasks are separate from one another, and therefore should end up in two separate functions. If we were write them into a single function, we would never be able to reuse our code as effectively as possible. We wouldn’t be able to add two numbers together without printing them, nor could we print a number without first adding it to another.

On a final note to allow functions to help us organise and improve the readability of our programs it is essential that our functions are given a meaningful name, this stems to our variables as well. We need to know what is being stored in a variable, or what a function we call does, and this is best coming across in the names we select for them.

Conclusion

In this article, we have learnt about functions. A function is a grouping of statements that can be referenced by name, that can accept and return data. Using the name of a function we can call it multiple times in different parts of our program. This results in cleaner, more organised programs, that avoid us having to write duplicate code when wanting to perform a similar task, in which only the data has changed.

After reading this article, and assuming you have been reading the rest of the series, you should have a good understanding of the major concepts that most languages are built around. In the final article in this series, we will look at combining all the concepts we have learn about so far, by introducing algorithms.

Programming Fundamentals: Repetition

Programming

043017_1840_Programming1.png

In the last article we looked at conditional statements, and how they allow for branching, the ability for a computer to decide what instructions to execute based on a set of conditions. The practice of evaluating conditions to dictate which instructions to execute or ignore, is only one application of conditions. Another way to use them is through repetition, the ability for a program to repeat instructions, in which they are repeated based on some condition.

A condition used in repetition, is used to determine how many times a computer should execute some instructions. In some cases, we know exactly how many times we want an instruction to repeat, and if it is only a few times, it is not difficult to type out the same instruction several times. Although when we want to repeat the execution of an instruction 10,000 times this would take a while to type out, and is an ideal situation where we would use repetition. Telling our program to execute the instructions under the condition that it executes them 10,000 times, which obviously saves us a whole lot of time!

In other cases, we may not know how many times we want instructions to repeat, but we know we need to execute them more than once. This maybe because we want to repeat them based on some user input, or based on the results of some other instructions, like a mathematical expression.

A good example program would be a video game. Games are very complicated programs, but the basic way in which they work involves using repetition. We start up our game, and run through the same instructions: asking for user input, updating the things that happen in the game such as characters and enemies moving or shooting, and finally displaying the game to the screen. We can play games for a matter of minutes or hours, and this straightforward process is repeated indefinitely through this time, until something occurs to cause game over. The exact condition in which game over occurs depends on the type of game, but this could be from losing all your lives, having no health left, or running out of time. Either way the game works by repeating a set of instructions until a game over condition evaluates to true, resulting in the game ending.

In terms of implementing repetition into our programs, like with conditional statements, all programming languages support repetition, using loops. There are several different types of loops but the two most common types are while, and for loops. A while loop looks like the following:

While (condition)

    Execute instructions

Looks a lot like an if statement, except we replace the word if with while. What happens is that our program executes instructions based on the condition. The important thing to remember about a while loop is that the instructions won’t be executed a single time if the condition is not met. While loops are best used when we don’t know how many times we need to iterate (pass)through a loop, for example:

While (gameOver does not equal true)

    Execute instructions

In the above example the instructions in the loop will be executed until the variable gameOver is changed to true, which could occur at any time. A for loop on the other hand looks like the following:

For (initialisation; condition; increment/decrement)

    Execute instructions

A for loop seems slightly more complicated than a while loop, but it’s not too difficult to understand. For loops are split into three parts: initialisation, condition, and increment/decrement. In the first part this is where we initialise any variables we would like to use in the loop, usually a variable that stores an integer value. Second is our condition, this obviously decides how many times we iterate through the loop, repeating instructions. Typically, this will be some comparison such as x < 10, or x equals 10. The final part relates to how we alter the variable we initialised, whether we add or subtract some value, dependent on our needs. Below is an example of a for loop.

For (integer i = 0; i < 10; i = i + 2)

    Execute instructions

In this example, we will execute our instructions 5 times. We start by initialising our i variable with the value 0 then we check if the value is less than 10, if it is then we add 2 to the variable then execute the instructions. This is repeated until i is no longer less than 10, which will occur after 5 iterations. It is best to use a for loop when we know exactly how many times we want our instructions to be repeated.

So far, we have looked at using loops as a way of allowing a computer to repeatedly execute instructions, but what types of instructions do we want to execute in a loop? Some examples include: adding or multiplying numbers, initialising variables, or even using loops to traverse through data structures.

The exact nature in which we traverse through a data structure depends on the programming language, and the type of data structure, but the general idea is that we can use a loop to traverse through all the data values we stored within a data structure. If we had a data structure that contained 100 different integer values, we could use a loop to traverse through the data structure, meaning we get access to each value, which we could then do something with, like change its value, or output it to the screen.

To conclude, repetition further enhances the capabilities of the programs we write by allowing us repeatedly execute instructions. Repeating instructions saves us from having to repeatedly type instructions in cases where we know the number of times we want an instruction to be executed. Additionally, they allow the creation of interactive applications such as games by allowing instructions to be executed several times unknown to the programmer. While and for loops are common implementations of repetition within programming languages, each tailoring to a specific situation, while loops best suited for situations where we do not know the number of times the instructions will be executed, and for loops reserved for times when we do know this information.

After reading this article you will be well on your way to understanding the fundamental principles required to write programs however there are still a few things left to learn. The next article will look at functions, what they are, and how they can be used to help us write better programs.