Get Started Now

Intro to C++ Programming

Starting from the fundamentals, become a C++ software engineer, step by step.

LATEST UPDATES

Screenshot from Cyberpunk 2077
Module One

Intro to C++ Programming

Starting from the basics, become a C++ software engineer, step by step

Screenshot from Cyberpunk 2077
Screenshot from The Witcher 3: Wild Hunt
Screenshot from Warhammer: Total War
Screenshot from Cyberpunk 2077
Module Two

SDL and C++ Development

Learn C++ and SDL development by recreating classic retro games

Screenshot from Cyberpunk 2077
Screenshot from The Witcher 3: Wild Hunt
Screenshot from Warhammer: Total War
Screenshot from Cyberpunk 2077
Module Three

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects

Screenshot from Cyberpunk 2077
Screenshot from The Witcher 3: Wild Hunt
Screenshot from Warhammer: Total War
FEATURED LESSONS

Creating Custom Iterators using C++20 Concepts

A detailed guide to implementing a custom iterator type from scratch, using modern recommended techniques
3D Character Concept Art
Ryan McCombe
Published

Previously, we implemented iterators for our custom container simply by forwarding the iterators from the custom type to which we were deferring storage.

Below, our custom type is using a std::vector to store its objects, so we can just forward any calls to begin() and end() to that underlying array:

class Party {
public:
  std::array<Character, 3> Array;
  auto begin() { return Array.begin(); }
  auto end() { return Array.end(); }
};

In this lesson, we will instead see how we can add iterators to our custom container ourselves, building them from scratch. This has three advantages:

  • It helps us learn more about iterators
  • Often we’ll want to implement iterators in situations where we don’t have some other underlying container that can bear the burden
  • Even if there is an underlying container that supports iterators, sometimes we’ll want our custom type’s iterators to behave differently from those of the underlying container

Let's get started!

Partition Algorithms

An introduction to partitions, and the C++ standard library algorithms that create them
3D Character Concept Art
Ryan McCombe
Published

A collection is partitioned if similar objects are grouped together, based on some rule that separates those objects into two groups.

For example, the following collection of numbers is partitioned into even and odd numbers. All of the even numbers are grouped at the start of the container, whilst the odd are grouped at the end:

4, -6, 2, 7, 1, 5

Specifically, we can imagine this partition is generated based on the following rule:

[](int x){
  return x % 2 == 0;
}

Such that in our collection, every element for which this function returns true (that is, every even number) occurs before every element for which the function returns false (that is, every odd number)

Partitioning vs Sorting

Partitioning a collection closely relates to the idea of sorting it. For example, the following collection is partitioned such that all negative numbers occur before all positive numbers:

-6, -1, -2, 7, 1, 5

Sorting the collection would also generate an ordering that obeys that same partitioning rule:

-6, -2, -1, 1, 5, 7

The key difference is that sorting a collection can require significantly more operations than partitioning it. As such, if our use case only requires partitioning our collection, we can get large performance benefits by not unnecessarily sorting everything.

This lesson introduces the 3 main standard library algorithms we have for creating partitioned collections:

  • partition(): Partitions a collection in-place
  • stable_partition(): Partitions a collection in place, whilst maintaining the previous relative order of elements within each partition
  • partition_copy(): Partitions a collection into two new collections

We also cover two other related algorithms, that can help us when working with partitions:

  • is_partitioned(): Returns a boolean representing whether a collection is partitioned
  • partition_point(): Determines the position within the collection where one partition ends and the other starts

All of these algorithms are within the <algorithm> header and belong to the std::ranges namespace.

Comparison Algorithms

An introduction to the 8 main comparison algorithms in the C++ standard library
zb721.jpg
Ryan McCombe
Published

In this lesson, we cover all of the main standard library algorithms that are used to compare two collections:

  • equal(): Determine if two collections contain the same objects, in the same order
  • is_permutation(): Determine if two collections contain the same objects, in any order
  • mismatch(): Find the first position where two collections deviate from each other
  • lexicographical_compare(): Determine if one collection is “less than” another collection.
  • lexicographical_compare_three_way(): Determine if one collection is less than, greater than or equal to another collection
  • starts_with(): Check if the objects in one collection match the objects at the start of another collection
  • ends_with(): Check if the objects in one collection match the objects at the end of another collection
  • includes(): Check if one collection contains another collection - that is, if one is a subset or superset of another

Let's get started!

Fully Dynamic Types using Void Pointers and std::any

An overview of how to create variables that can store any date type, using void pointers and std::any
3D Character Concept Art
Ryan McCombe
Published

In this lesson, we’ll cover the two main techniques for implementing dynamic types in C++. A variable with a dynamic type can store different types of data throughout its life. For example, it might be initialized as an int, have a float assigned to it later, and then finally changed to store a Player or some other custom type.

However, we should remember that C++ is designed as a statically typed language. The use of dynamic types, particularly unconstrained dynamic types, should be quite rare.

  • If we don’t know the type of data we are working with, but it can be deduced at compile time, templates tend to be the tool we should prefer.
  • If we need dynamic types at run time, we’ll often be able to narrow it down to a range of possibilities. In this case, we should generally prefer specifying those possibilities, using the techniques we covered in the previous lesson - such as std::variant.

Use Cases

There are some use cases where neither of these options apply, and we need to create an unconstrained container. Some examples include:

  • Arbitrary user data: such as creating a file manager, where we have no way of predicting what type of data the users will be providing
  • Developer tools: for performance reasons, the tools that support programming in general are often written in C++. Examples include our IDE, and interpreters that implement other languages such as Python and JavaScript. We have no way to know in advance what types will be created, and many of those programming languages are designed around a dynamic type system anyway
  • Message passing: components that coordinate other systems are often written in C++. An example of this is a messaging component that facilitates communication between other systems. Our component may not need to operate on these messages in any way, so attempting to restrict or coerce them into a known type is unnecessary.

To deal with these scenarios, we have void pointers, or containers that are based on void pointers. An example of such a container is the standard library’s std::any, introduced in C++17. We’ll cover both void pointers and std::any in this lesson.

Fold Algorithms

An introduction to the 6 new folding algorithms added in C++23, providing alternatives to std::reduce and std::accumulate
aSDL7.jpg
Ryan McCombe
Published

Similar to std::reduce() and std::accumulate() from the previous lesson, these algorithms are designed to work on collections of objects and to return a single result.

For example, were we to have a collection of objects in our shopping basket, and we wanted to total cost of all the objects, the fold algorithms are ideal.

They give us some additional options over std::reduce() and std::accumulate(). Most notably, there are 8 different variants, giving us more control over how our collection is combined.

They’re also range-based algorithms, thereby giving us more control over how we define the collection of objects that we want to be the input.

All the algorithms in this lesson are available within the <algorithm> header:

#include <algorithm>

Reduce and Accumulate

A detailed guide to generating a single object from collections using the std::reduce and std::accumulate algorithms
3D Character Concept Art
Ryan McCombe
Published

In this lesson and the next, we introduce a range of algorithms that are designed to simplify large collections of objects into simpler outputs.

For example, we might have a collection of objects representing bank transactions, and we want to generate a simple object that includes some aggregate data. That could perhaps include information like:

  • What was the total amount of all transactions
  • How many transactions were there
  • What is the average transaction value

In this lesson, we’ll introduce the reduce() and accumulate() algorithms, which remain the most popular way of implementing logic like this.

In the next lesson, we’ll introduce fold algorithms, which were added to the language in C++23, and give us more ways to accomplish tasks like this.

Ranges and Sentinels

A practical guide to defining ranges using sentinel values, and why we sometimes need to
3D Character Concept Art
Ryan McCombe
Published

In earlier lessons, we saw how any container with appropriate begin() and end() methods is a range.

Meanwhile, other pieces of code, such as algorithms, can be designed to work with ranges. This allows our code to be decoupled - an important design pattern to make complex projects more manageable.

For example, the creator of the container and the creator of the algorithm do not need to coordinate their work. If they both know what a range is, they can write their systems accordingly, and they will naturally be compatible with each other.

Below, we use a std::vector, which is a range, alongside the std::ranges::for_each() algorithm, which works with any range.

The algorithm passes every object in our range to a function, which we provide as the second argument:

#include <algorithm>
#include <iostream>
#include <vector>

void Log(int x){ std::cout << x << ", "; } 

int main(){
  std::vector R{1, 4, 3, 8, -2, 5};
  std::ranges::for_each(R, Log); 
}
1, 4, 3, 8, -2, 5,

There is a second way to define a range, which involves defining an iterator where our range starts, and a sentinel which signals when our range ends.

We’ll cover this pattern in this lesson, and explain the reasons why we would need to use it.

List, Aggregate, and Designated Initialization

A quick guide to creating objects using lists, including std::initializer_list, aggregate and designated initialization
Character Concept Art
Ryan McCombe
Published

At this point, we’re likely very familiar with initializing objects by specifying the type of object we want to construct, and a list of values to pass to a constructor defined as part of that type.

Typically, this looks something like this:

#include <iostream>

struct Vec3 {
  // Constructor using a member initializer list
  Vec3(int x, int y, int z)
    : x{x}, y{y}, z{z}
  {}

  int x{0};
  int y{0};
  int z{0};

  void Log(){
    std::cout
      << "x=" << x
      << ", y=" << y
      << ", z=" << z << '\n';
  }
};

int main(){
  // Calling the constructor to create an object
  Vec3 Vector{1, 2, 3};
  Vector.Log();
}
x=1, y=2, z=3

This is referred to as list initialization, and the previous example is only one way to use it.

In this lesson, we’ll cover some of the other useful scenarios. We’ll also introduce other forms of list initialization, including aggregate initialization, and designated initialization.

Dynamic Data Types using std::variant and Unions

A guide to storing one of several different data types within the same container, using unions and variants
3D Character Concept Art
Ryan McCombe
Published

Often, we’ll come across scenarios where we need a variable to contain one of several possible data types. For example, we may need a variable that can store either an int or a float.

We could solve this problem with a class or struct, that simply stores our types as different members:

struct Number {
  int i;
  float f;
};

But this is a little wasteful of resources. Creating an object from this struct requires the compiler to allocate enough memory to store both an int and a float. But our use case doesn’t require that - we only need to store either an int or a float.

Allocating memory for the combination of all the types is unnecessary, and it gets increasingly wasteful when our struct is declaring more, or larger, types.

Instead, we solve problems like these using unions.

Working with String Views

An in-depth guide to std::string_view, including their methods, operators, and how to use them with standard library algorithms
3D Character Concept Art
Ryan McCombe
Published

In this lesson, we expand our knowledge of std::string_view by exploring its methods and operators. This includes working with the individual characters of a string view, analyzing its contents, shrinking its purview, and more.

We also show some practical examples of how string views can interact with other parts of the standard library.

This includes standard library views, algorithms, regular expressions, and anything that works with iterators.

This builds upon our previous introduction to string views, so familiarity with what was covered there is assumed:

Module 1
3D art showing a progammer setting up a development environment

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access!

This course includes:

  • 66 Lessons
  • Over 200 Quiz Questions
  • Capstone Project
  • Regularly Updated
  • Help and FAQ
Module 2
DreamShaper_v7_cyberpunk_woman_playing_video_games_modest_clot_0.jpg

Making Games with SDL

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Under Construction
Module 3
7a.jpg

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access!

This course includes:

  • 106 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Get Started Now

Intro to C++ Programming

Starting from the fundamentals, become a C++ software engineer, step by step.

Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved