Lecture 1 / 12
Lecture 01 · Foundations

What is C++?

Beginner ~35 min No prerequisites

Introduction

C++ is a compiled, general-purpose programming language created by Bjarne Stroustrup in 1985 as an extension of the C language. It adds object-orientation, generic programming, low-level memory control and a massive standard library.

C++ is one of the most widely used programming languages in the world and is known for its speed, efficiency, and flexibility.

It is commonly used in:

  • Game development
  • Operating systems
  • Embedded systems
  • Desktop applications
  • Compilers and browsers
  • High-performance software
✅ Quick fact
C++ is used in major software systems including game engines, browsers, databases, and operating systems.

History of C++

C++ was developed by Bjarne Stroustrup at Bell Labs.

  • 1979 – Development of “C with Classes” began.
  • 1985 – The language was officially named C++.
  • 1998 – First ISO standard (C++98).
  • 2011 – Modern C++ introduced with C++11.
  • 2020 – C++20 added modules, ranges, concepts, and more.

Why Learn C++?

  • Excellent performance and speed
  • Strong understanding of memory and computer systems
  • Widely used in industry and competitive programming
  • Powerful object-oriented programming support
  • Huge ecosystem and standard library
  • Foundation for learning advanced programming concepts

Key Characteristics

  • Compiled – source code is turned into native machine code.
  • Statically typed – type checking is done at compile time.
  • Supports multiple paradigms – procedural, OOP, generic, functional.
  • Performance-critical – fine-grained control over memory and resources.

Understanding Compiled Languages

C++ programs must be compiled before they can run.

The compilation process converts human-readable source code into machine code that the computer can execute.

Compilation process

  1. Write source code (.cpp files)
  2. Compile the code using a compiler
  3. Generate an executable program
  4. Run the executable
ℹ️ Source code vs executable
Source code is the code you write. The executable is the final machine-readable program created by the compiler.

Structure of a C++ Program

Most C++ programs contain:

  • Header files
  • The main() function
  • Statements and expressions
  • Output/input operations

Hello World – Your First C++ Program

Save the following as hello.cpp, compile it, and run it.

hello.cpp
#include <iostream>

int main() {

    std::cout << "Hello, World!"
              << std::endl;

    return 0;
}

Understanding the Code

Code Purpose
#include <iostream> Imports the standard input/output library
main() The starting point of every C++ program
std::cout Prints output to the console
return 0; Indicates successful program execution
💡 Important
Every C++ program starts execution from the main() function.

Compiling and Running the Program

Compile and run using GCC:

g++ -std=c++20 hello.cpp -o hello

./hello

The program output will be:

Hello, World!

Choosing a Toolchain

For learning you can pick any of these:

  • GCC – the GNU Compiler Collection (Linux/macOS/WSL).
  • Clang – LLVM-based compiler with excellent error messages.
  • MSVC – Microsoft Visual C++ compiler for Windows.
  • Online IDEs – useful for learning without installation.

Popular Online Compilers

  • Compiler Explorer
  • OnlineGDB
  • Replit
  • Programiz Compiler

Verifying Installation

g++ --version

clang++ --version

cl
ℹ️ IDE Recommendation

VS Code with the C/C++ extension or JetBrains CLion provide syntax highlighting, debugging, IntelliSense, and project management tools.

Compiling with Flags

Compiler flags allow you to control warnings, optimizations, debugging, and language standards.

Common useful flags

  • -Wall -Wextra -Wpedantic – enable most warnings.
  • -O2 – optimisation level.
  • -std=c++20 – select the language standard.
  • -g – include debug symbols for debugging tools.
compile-command.sh
g++ -Wall -Wextra -std=c++20 -O2 hello.cpp -o hello

Comments in C++

Comments are notes written inside the source code for explanation and readability.

comments.cpp
// Single-line comment

/*
   Multi-line
   comment
*/

int main() {
    return 0;
}

C++ File Extensions

Extension Description
.cpp C++ source file
.h Header file
.hpp C++ header file
.exe Executable file (Windows)

⚙️ Try It Yourself - C++ Compiler

Practice C++ programming right here in your browser! Modify the code below and click "Compile & Run" to see the results instantly.

💡 Practice Tips:

  • Try changing the output message in the Hello World program
  • Experiment with different data types like int, double, and string
  • Add variables and practice arithmetic operations
  • Try creating multiple output statements using std::cout
  • Use Ctrl+Enter to quickly compile and run your code

Real-world Applications of C++

  • Game engines like Unreal Engine
  • Browsers such as Chrome and Firefox
  • Databases like MySQL
  • Operating systems and drivers
  • Financial and trading systems
  • Embedded and robotics systems
🎯 Exercise 1.1

Install a C++ compiler (GCC, Clang or MSVC), write the Hello World program, compile it, and verify the output.

🎯 Exercise 1.2

Modify the Hello World program so it prints:

Hello, C++!
Welcome to programming.
🎯 Exercise 1.3

Create a simple project with two source files:

  • main.cpp
  • utils.cpp

Compile them together into a single executable program.

🚀 Coming Up Next
In the next lecture, you'll learn variables, data types, user input, and basic operators in C++.
Lecture 02 · Foundations

Setting up the Compiler

Beginner ~40 min Requires: Lecture 01

Why Do You Need a Compiler?

C++ is a compiled programming language. This means the source code you write must be converted into machine code before the computer can execute it.

A compiler translates your C++ code into an executable program.

💡 Important
Without a compiler, the computer cannot understand or run your C++ source code.

Popular C++ Compilers

Compiler Platform Description
GCC Linux / Windows / macOS Open-source GNU Compiler Collection
Clang Linux / macOS / Windows LLVM-based compiler with excellent diagnostics
MSVC Windows Microsoft Visual C++ compiler included with Visual Studio

Choosing an IDE or Code Editor

You can write C++ code in any text editor, but modern IDEs provide features like syntax highlighting, debugging, and auto-completion.

Popular options

  • VS Code – lightweight and beginner-friendly
  • Visual Studio – powerful IDE for Windows
  • CLion – professional JetBrains IDE
  • Code::Blocks – simple IDE for beginners
  • Dev-C++ – lightweight Windows IDE
ℹ️ Recommended Setup

VS Code with the C/C++ extension and GCC compiler is one of the most popular setups for beginners.

Installing GCC on Windows

On Windows, GCC is commonly installed using MinGW-w64.

Installation steps

  1. Download MinGW-w64 installer
  2. Install the compiler
  3. Add the compiler path to the system PATH variable
  4. Restart the terminal

Verify installation

g++ --version

If installed correctly, the compiler version will appear.

Installing GCC on Linux

Most Linux distributions provide GCC through package managers.

Ubuntu / Debian

sudo apt update
sudo apt install g++

Fedora

sudo dnf install gcc-c++

Arch Linux

sudo pacman -S gcc

Installing Clang on macOS

macOS users can install Clang using Xcode Command Line Tools.

xcode-select --install

Verify installation:

clang++ --version

Installing Visual Studio (MSVC)

Windows users can also use Microsoft's official C++ compiler.

Steps

  1. Download Visual Studio Community Edition
  2. Select the “Desktop development with C++” workload
  3. Complete installation

MSVC includes:

  • C++ compiler
  • Debugger
  • Build tools
  • Windows SDK

Creating Your First Project

Create a folder called cpp-project and inside it create a file named main.cpp.

main.cpp
#include <iostream>

int main() {

    std::cout << "Compiler setup successful!"
              << std::endl;

    return 0;
}

Compiling the Program

Open the terminal inside your project folder and run:

g++ main.cpp -o main

This command:

  • Compiles main.cpp
  • Creates an executable named main

Running the Program

Linux / macOS

./main

Windows

main.exe

Expected output:

Compiler setup successful!

Understanding Compiler Errors

Compilers display errors when the code contains mistakes.

Example error

error.cpp
#include <iostream>

int main() {

    std::cout << "Hello"

    return 0;
}

This program is missing a semicolon (;) after the output statement.

💡 Debugging Tip
Read compiler errors carefully. Most beginner mistakes are caused by missing semicolons, brackets, or spelling errors.

Useful Compiler Flags

Flag Purpose
-Wall Enable common warnings
-Wextra Enable extra warnings
-std=c++20 Use C++20 standard
-g Generate debug information
-O2 Enable optimization

Compiling with Flags Example

g++ -Wall -Wextra -std=c++20 main.cpp -o main

Online Compilers

If you do not want to install software immediately, you can use online compilers.

  • OnlineGDB
  • Replit
  • Compiler Explorer
  • Programiz
🌐 Online Compiler Advantage

Online compilers are useful for practice and quick testing without local installation.

⚙️ Try It Yourself - Compiler Test

Modify the program below and test your compiler setup directly in the browser.

💡 Practice Tips:

  • Change the output text
  • Add multiple std::cout statements
  • Experiment with syntax errors and observe compiler messages
  • Practice compiling with different flags
  • Try creating multiple source files
🎯 Exercise 2.1

Install a C++ compiler on your operating system and verify the installation using g++ --version or clang++ --version.

🎯 Exercise 2.2

Create a project folder with a main.cpp file, compile it manually using terminal commands, and run the executable.

🎯 Exercise 2.3

Compile a program using:

g++ -Wall -Wextra -std=c++20 main.cpp -o main

Observe any warnings generated by the compiler.

🚀 Coming Up Next
In the next lecture, you'll learn variables, data types, constants, and user input in C++.
Lecture 03 · Foundations

Variables & Data Types

Beginner ~45 min Requires: Lecture 02

Introduction to Variables

Variables are named containers used to store data in memory. Every program uses variables to keep track of values such as numbers, characters, prices, marks, temperatures, or user input.

In C++, every variable must have:

  • A type — defines what kind of data can be stored
  • A name — used to access the value later
  • An optional initial value

Example:

intro.cpp
int age = 20;
double salary = 45000.50;
char grade = 'A';

Here:

  • age stores an integer value
  • salary stores a decimal value
  • grade stores a single character

Fundamental Types

C++ provides several built-in primitive data types. Choosing the correct type helps optimise memory usage and improves program performance.

Type Size (typical) Example
bool 1 byte bool ok = true;
char 1 byte char c = 'A';
int 4 bytes int i = 42;
long / long long 8 bytes long long big = 123456789LL;
float 4 bytes float f = 3.14f;
double 8 bytes double d = 2.71828;

Understanding Integer Types

Integer types are used for storing whole numbers without decimal points. Different integer types exist because programs may need to store very small or very large values.

  • short → smaller range, uses less memory
  • int → most commonly used integer type
  • long and long long → used for large numeric values

Example use cases:

Scenario Recommended Type
Age of a student int
Population of a country long long
Game score int

Floating-Point Types

Floating-point types are used for decimal numbers.

  • float → lower precision, uses less memory
  • double → higher precision, commonly preferred

Examples:

floating.cpp
float temperature = 36.5f;
double pi = 3.1415926535;

The suffix f tells the compiler that the value is a float.

Character and Boolean Types

The char type stores a single character enclosed in single quotes.

char.cpp
char letter = 'G';
char symbol = '#';

The bool type stores logical values:

bool.cpp
bool isLoggedIn = true;
bool gameOver = false;

Signed vs Unsigned

Appending u makes an integer type unsigned (unsigned int or uint32_t). Unsigned types cannot represent negative values, which can be useful for bit-wise operations.

Signed integers can store both positive and negative numbers:

signed.cpp
int temperature = -10;
unsigned int score = 100;

If you try to store a negative number in an unsigned variable, the result may not be what you expect due to overflow.

Type Modifiers

Keywords that affect size and sign:

  • short
  • long, long long
  • signed (default for int)
  • unsigned

These modifiers allow programmers to control memory usage and numeric range more precisely.

Variable Naming Rules

C++ variable names must follow certain rules:

  • Names can contain letters, digits, and underscores
  • Names cannot begin with a number
  • Spaces are not allowed
  • C++ keywords cannot be used as variable names
  • Variable names are case-sensitive
Valid Names Invalid Names
totalMarks 2marks
user_name user name
price1 float

Variable Declaration & Initialisation

Declaration tells the compiler about the variable type and name. Initialisation assigns the first value to the variable.

variables.cpp
int count = 0;                // initialise to zero
double price = 19.99;
char grade = 'A';
bool isReady = true;

unsigned int mask = 0xFF;
long bigNum = 123456789L;

Multiple Variable Declarations

You can declare multiple variables of the same type in one line:

multiple.cpp
int x = 10, y = 20, z = 30;

However, for readability, many programmers prefer declaring one variable per line.

The sizeof Operator

The sizeof operator returns the number of bytes occupied by a type or variable in memory.

sizeof.cpp
cout << sizeof(int);
cout << sizeof(double);

This is useful when working with memory management, arrays, and system-level programming.

🎯 Exercise 3.1

Write a program that declares one variable of each primitive type, prints their size using sizeof, and then swaps the values of two integer variables without using a temporary variable.

🎯 Exercise 3.2

Create a simple student information program using variables for:

  • Student name initial
  • Age
  • Marks
  • Percentage
  • Pass status

Print all values using cout.

Lecture 04 · Foundations

Control Flow

Beginner ~45 min Requires: Lecture 03

Introduction to Control Flow

Control flow determines the order in which program instructions are executed. By default, C++ executes code line by line from top to bottom. Control statements allow programs to make decisions, repeat tasks, and react differently depending on conditions.

Control flow is mainly divided into:

  • Decision-making statementsif, else, switch
  • Loopsfor, while, do-while
  • Jump statementsbreak, continue, return

Boolean Conditions

Control flow statements work using conditions that evaluate to either true or false.

Examples of comparison operators:

Operator Meaning Example
== Equal to a == b
!= Not equal to a != b
> Greater than x > 10
< Less than x < 5
>= Greater than or equal marks >= 40
<= Less than or equal age <= 18

If / else

The if statement executes a block of code only if the condition is true.

IfDemo.cpp
int score = 85;

if (score >= 90) {
    std::cout << "Grade A" << std::endl;
} else if (score >= 80) {
    std::cout << "Grade B" << std::endl;
} else {
    std::cout << "Needs improvement" << std::endl;
}

In this example:

  • If score is 90 or above → Grade A
  • If score is between 80 and 89 → Grade B
  • Otherwise → Needs improvement

Nested If Statements

An if statement can exist inside another if statement. This is called nesting.

NestedIf.cpp
int age = 20;
bool hasID = true;

if (age >= 18) {
    if (hasID) {
        std::cout << "Entry allowed";
    }
}

Logical Operators

Logical operators combine multiple conditions.

Operator Meaning Example
&& Logical AND age > 18 && hasID
|| Logical OR marks > 40 || sportsQuota
! Logical NOT !isGameOver

Switch Statement

The switch statement is used when a variable can have multiple possible fixed values.

SwitchDemo.cpp
char choice = 'b';

switch (choice) {
    case 'a':
    case 'b':
    case 'c':
        std::cout << "Option a–c selected" << std::endl;
        break;
    default:
        std::cout << "Other option" << std::endl;
}

The break statement prevents execution from continuing into the next case.

Fallthrough in Switch

If break is omitted, execution continues into the next case. This behaviour is called fallthrough.

Fallthrough.cpp
int day = 1;

switch (day) {
    case 1:
        std::cout << "Monday";
    case 2:
        std::cout << " Tuesday";
}

Output:

output.txt
Monday Tuesday

Loops

Loops repeat a block of code multiple times until a condition becomes false.

  • for (init; condition; increment)
  • while (condition)
  • do { … } while (condition);
  • Range-based for (auto &v : container)

For Loop

The for loop is commonly used when the number of iterations is known.

LoopDemo.cpp
int sum = 0;

for (int i = 1; i <= 5; ++i) {
    sum += i;
}
std::cout << "Sum = " << sum << std::endl;

This loop runs 5 times and calculates the total sum from 1 to 5.

While Loop

The while loop runs as long as the condition remains true.

WhileLoop.cpp
int i = 1;

while (i <= 5) {
    std::cout << i << " ";
    ++i;
}

Do-While Loop

The do-while loop executes the code block at least once before checking the condition.

DoWhile.cpp
int num = 1;

do {
    std::cout << num << " ";
    ++num;
} while (num <= 5);

Range-Based For Loop

C++11 introduced range-based loops for easier iteration through containers such as arrays and vectors.

RangeLoop.cpp
int numbers[] = {1, 2, 3, 4};

for (int n : numbers) {
    std::cout << n << " ";
}

Break and Continue

The break statement immediately terminates a loop, while continue skips the current iteration.

BreakContinue.cpp
for (int i = 1; i <= 10; ++i) {

    if (i == 5) {
        continue;
    }

    if (i == 8) {
        break;
    }

    std::cout << i << " ";
}

Infinite Loops

An infinite loop never stops unless interrupted manually or with a control statement.

InfiniteLoop.cpp
while (true) {
    // runs forever
}

Infinite loops are useful in game engines, servers, and event-driven applications.

🎯 Exercise 4.1

Write a program that prints the first 20 Fibonacci numbers using a while loop.

🎯 Exercise 4.2

Create a number guessing program where the user keeps entering values until the correct number is guessed.

🎯 Exercise 4.3

Print the multiplication table of a number entered by the user using a for loop.

Lecture 05 · Foundations

Functions

Beginner ~40 min Requires: Lecture 04

Introduction to Functions

Functions are reusable blocks of code designed to perform a specific task. Instead of writing the same logic multiple times, programmers create functions and call them whenever needed.

Functions improve:

  • Code reusability
  • Readability
  • Program organisation
  • Debugging and maintenance

Examples of common functions:

  • Calculating totals
  • Checking passwords
  • Printing menus
  • Sorting data
  • Performing mathematical operations

Function Syntax

General form: return_type name(parameter_list) { body }

A function typically contains:

Part Description
return_type Type of value returned by the function
name Name used to call the function
parameter_list Input values accepted by the function
body Code executed when the function is called
MathUtils.cpp
int add(int a, int b) {
    return a + b;
}

double average(const std::vector<int>& values) {
    int sum = 0;

    for (int v : values)
        sum += v;

    return static_cast<double>(sum) / values.size();
}

Calling Functions

Functions only execute when they are called.

CallFunction.cpp
int result = add(5, 3);

std::cout << result;

Here, the values 5 and 3 are passed to the function as arguments.

Parameters vs Arguments

These two terms are often confused by beginners.

Term Meaning
Parameter Variable defined in the function declaration
Argument Actual value passed to the function
Arguments.cpp
int multiply(int a, int b) {
    return a * b;
}

multiply(4, 6);

In this example:

  • a and b are parameters
  • 4 and 6 are arguments

Void Functions

A function that does not return any value uses the void keyword.

VoidFunction.cpp
void greet() {
    std::cout << "Welcome to C++";
}

Return Statement

The return statement sends a value back to the caller.

ReturnDemo.cpp
double square(double x) {
    return x * x;
}

Once return executes, the function immediately ends.

Function Declaration and Definition

Functions can be declared before main() and defined later. This is called a function prototype.

Prototype.cpp
int sum(int, int);

int main() {
    sum(2, 3);
}

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

Function Overloading

Function overloading allows multiple functions to have the same name but different parameter types or counts.

OverloadDemo.cpp
int max(int a, int b) {
    return (a > b) ? a : b;
}

double max(double a, double b) {
    return (a > b) ? a : b;
}

The compiler selects the correct function based on the argument types.

Default Arguments

Default arguments supply a value that is used when the caller omits that argument.

DefaultArgs.cpp
void greet(const std::string& name = "World") {
    std::cout << "Hello, " << name << "!\n";
}

Calling greet() prints:

output.txt
Hello, World!

Pass by Value

By default, C++ passes arguments by value, meaning a copy of the variable is sent to the function.

PassByValue.cpp
void changeValue(int x) {
    x = 100;
}

int num = 10;
changeValue(num);

The original value of num remains unchanged because only a copy was modified.

Pass by Reference

References allow functions to modify the original variable directly.

PassByReference.cpp
void increment(int& value) {
    ++value;
}

Reference parameters are efficient for large objects because they avoid unnecessary copying.

Recursive Functions

A recursive function calls itself repeatedly until a stopping condition is reached.

Recursion.cpp
int factorial(int n) {

    if (n == 0) {
        return 1;
    }

    return n * factorial(n - 1);
}

Recursive functions must always include a base condition to prevent infinite recursion.

Inline Functions

The inline keyword suggests that the compiler replace the function call with the actual code to reduce overhead.

Inline.cpp
inline int cube(int x) {
    return x * x * x;
}

Function Best Practices

  • Keep functions small and focused
  • Use meaningful function names
  • Avoid duplicate logic
  • Use comments where necessary
  • Prefer passing large objects by reference
🎯 Exercise 5.1

Implement a factorial function (recursive) for int and an overloaded version that works with unsigned long long.

🎯 Exercise 5.2

Create functions for:

  • Finding the square of a number
  • Checking whether a number is even or odd
  • Finding the largest of three numbers
🎯 Exercise 5.3

Create a menu-driven calculator using functions for addition, subtraction, multiplication, and division.

Lecture 06 · Core Concepts

Object-Oriented Programming

Intermediate ~55 min Requires: Lecture 05

Introduction to Object-Oriented Programming

Object-Oriented Programming (OOP) is a programming paradigm that organizes code into objects. An object combines both data and the functions that operate on that data. OOP helps developers build applications that are easier to maintain, reuse, and scale.

Modern software systems such as games, banking apps, operating systems, and web applications heavily rely on OOP concepts. In C++, OOP provides powerful tools for structuring large programs into reusable components.

The four major principles of OOP are:

  • Encapsulation → Protecting data inside classes
  • Abstraction → Hiding implementation details
  • Inheritance → Reusing existing classes
  • Polymorphism → One interface, multiple behaviors
💡 Real-World Analogy: Think of a car. The driver only interacts with the steering wheel, pedals, and dashboard. The internal engine implementation is hidden. This is similar to abstraction in OOP.

What is a Class?

A class acts as a blueprint for creating objects. It defines what data an object stores and what actions it can perform.

For example, a Person class may store a person's name and age, while also providing functions such as displaying information or updating the age.

Class Declaration

Person.hpp
class Person {
private:
    std::string name;
    int age;

public:
    Person(const std::string& name, int age)
        : name(name), age(age) {}

    const std::string& getName() const { return name; }
    int getAge() const { return age; }

    void birthday() { ++age; }
};

Understanding Access Specifiers

Access specifiers control which parts of a class can be accessed from outside the class.

  • private → Accessible only inside the class
  • public → Accessible from anywhere
  • protected → Accessible in derived classes
⚠️ Important: Data members are usually kept private to protect them from accidental modification. This practice is known as encapsulation.

Constructors

A constructor is a special member function automatically called when an object is created. Constructors are mainly used to initialize object data.

ConstructorExample.cpp
Person p("Alice", 25);

std::cout << p.getName()
          << " is "
          << p.getAge()
          << " years old"
          << std::endl;

Member Functions

Member functions define the behavior of a class. In the Person class, functions like getName(), getAge(), and birthday() operate on the object's internal data.

The keyword const after a function means that the function does not modify the object.

Inheritance

Inheritance allows one class to acquire properties and behaviors from another class. This promotes code reuse and creates logical relationships between classes.

A derived class inherits members from a base class. In the following example, Employee inherits from Person.

Employee.hpp
class Employee : public Person {
private:
    std::string department;

public:
    Employee(const std::string& name,
             int age,
             const std::string& dept)
        : Person(name, age), department(dept) {}

    const std::string& getDept() const { return department; }
};

Types of Inheritance

  • Single Inheritance → One derived class inherits from one base class
  • Multilevel Inheritance → A derived class becomes a base class for another class
  • Multiple Inheritance → One class inherits from multiple base classes
  • Hierarchical Inheritance → Multiple classes inherit from one base class
⚠️ Note: Multiple inheritance can make programs more complex. It should be used carefully in large projects.

Polymorphism

Polymorphism allows the same function name to behave differently depending on the object. This is one of the most powerful features of OOP.

There are two major types of polymorphism in C++:

  • Compile-time Polymorphism → Function overloading and operator overloading
  • Run-time Polymorphism → Virtual functions and method overriding

Polymorphism (virtual functions)

ShapeDemo.cpp
class Shape {
public:
    virtual double area() const = 0;
    virtual ~Shape() {}
};

class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}
    double area() const override {
        return 3.14159 * radius * radius;
    }
};

int main() {
    std::vector<std::unique_ptr<Shape>> shapes;
    shapes.push_back(std::make_unique<Circle>(2.5));

    for (const auto& s : shapes) {
        std::cout << "Area = " << s->area() << std::endl;
    }
}

Abstract Classes

The Shape class in the previous example is an abstract class because it contains a pure virtual function:

PureVirtual.cpp
virtual double area() const = 0;

Abstract classes cannot be instantiated directly. They serve as templates for derived classes.

Benefits of OOP

  • Improves code organization
  • Encourages code reuse
  • Makes applications easier to maintain
  • Helps manage large projects efficiently
  • Provides better security through encapsulation

Common Beginner Mistakes

  • Forgetting to make functions virtual when overriding behavior
  • Using public data members everywhere
  • Not understanding constructor initialization lists
  • Confusing objects with classes
  • Forgetting to use override in derived classes
🎯 Exercise 6.1

Model a simple library system: a base class Item with id and title, then derive Book and Magazine. Override a virtual printInfo() method for each derived class.

🚀 Challenge Exercise

Create a base class Vehicle with a virtual method startEngine(). Derive classes such as Car, Bike, and Truck, then override the method in each derived class.

🧠 Interview Insight: OOP is one of the most commonly asked topics in technical interviews. Questions related to inheritance, virtual functions, constructors, and polymorphism appear frequently in C++ interviews.
Lecture 07 · Core Concepts

Standard Library Containers

Intermediate ~50 min Requires: Lecture 06

Why use containers?

They provide generic, memory‑safe collections with well‑defined complexity guarantees.

Sequence containers

  • std::vector<T> – dynamic array, contiguous memory.
  • std::list<T> – doubly‑linked list.
  • std::deque<T> – double‑ended queue.
  • std::forward_list<T> – singly‑linked list (C++11+).

Associative containers

  • std::set<T> – ordered unique keys.
  • std::map<Key, Value> – ordered key/value pairs.
  • std::unordered_set<T> – hash‑based set (C++11+).
  • std::unordered_map<Key, Value> – hash table.

Basic usage examples

VectorDemo.cpp
#include <vector>
#include <iostream>

int main() {
    std::vector<int> numbers = { 1, 2, 3 };
    numbers.push_back(4);

    for (int n : numbers) {
        std::cout << n << ' ';
    }
    std::cout << std::endl;
}
MapDemo.cpp
#include <map>
#include <string>
#include <iostream>

int main() {
    std::map<std::string, int> scores;
    scores["Alice"] = 95;
    scores["Bob"]   = 87;

    for (const auto& kv : scores) {
        std::cout << kv.first << ": " << kv.second << std::endl;
    }
}
🎯 Exercise 7.1

Create a program that reads a list of words from the console, stores them in a std::unordered_set, and then reports how many unique words were entered.

Lecture 08 · Core Concepts

Templates & Generics

Intermediate ~55 min Requires: Lecture 07

What are Templates?

Templates are one of the most powerful features in C++. They allow you to write code once and reuse it with different data types. Instead of creating separate functions for int, float, or std::string, templates let the compiler generate the correct code automatically.

This concept is called generic programming. The goal is to write flexible and reusable code while maintaining high performance and type safety.

💡 Key Idea
Templates are processed at compile time. The compiler generates concrete versions of template code for every type you use.

Why Templates Matter

  • Reduce code duplication
  • Create reusable and scalable components
  • Provide strong type safety
  • Enable high-performance generic programming
  • Power the entire Standard Template Library (STL)

Function templates

Function templates allow functions to work with multiple data types without rewriting logic.

SwapTemplate.cpp
template<typename T>
void swapValues(T& a, T& b) {
    T tmp = a;
    a = b;
    b = tmp;
}

int main() {
    int x = 5, y = 10;
    swapValues(x, y);                // works for ints

    std::string s1 = "foo", s2 = "bar";
    swapValues(s1, s2);               // works for std::string
}

The keyword typename T declares a placeholder type called T. Whenever the function is called, the compiler automatically replaces T with the actual type being used.

✅ Best Practice
Use descriptive template parameter names when multiple template types are involved. For example: KeyType, ValueType, or Iterator.

How Template Type Deduction Works

Most of the time, the compiler can automatically determine template types based on the function arguments. This process is called template type deduction.

Deduction.cpp
template<typename T>
T square(T value) {
    return value * value;
}

int result = square(5);
double pi = square(3.14);

Here, the compiler automatically detects whether T should become int or double.

Multiple Template Parameters

Templates can accept multiple types simultaneously. This is useful when working with pairs, maps, or generic utility classes.

PairTemplate.cpp
template<typename T1, typename T2>
class Pair {
public:
    T1 first;
    T2 second;

    Pair(T1 a, T2 b) : first(a), second(b) {}
};

This allows a single class to hold two completely different data types.

Class templates

Class templates allow entire classes to become generic. Many STL containers such as std::vector, std::array, and std::map are implemented using templates.

SimpleVector.hpp
template<typename T, size_t N>
class SimpleVector {
    T data[N];

public:
    constexpr size_t size() const { return N; }

    T& operator[](size_t i) { return data[i]; }
    const T& operator[](size_t i) const { return data[i]; }
};

This template accepts two parameters:

  • T → the type of elements stored
  • N → the fixed size of the container

Using Class Templates

Once defined, templates can be instantiated with different types.

UseVector.cpp
SimpleVector<int, 5> numbers;
SimpleVector<double, 3> values;

numbers[0] = 42;
values[0] = 3.14;

The compiler generates separate versions of the class for each type combination.

Non-Type Template Parameters

Templates are not limited to data types. They can also accept compile-time constant values such as integers or sizes.

ArrayPrinter.cpp
template<typename T, int Size>
void printArray(T (&arr)[Size]) {
    for(int i = 0; i < Size; ++i)
        std::cout << arr[i] << " ";
}

Here, Size becomes part of the template definition itself.

Template specialization

Provide a custom implementation for a particular type.

template<>
struct std::hash<MyType> {
    size_t operator()(const MyType& obj) const noexcept { … }
};

Template specialization is useful when a specific type requires optimized or unique behavior.

Full vs Partial Specialization

C++ supports different forms of specialization:

  • Full specialization: Replaces the implementation for one exact type.
  • Partial specialization: Replaces only part of a template pattern.
Specialization.cpp
template<typename T>
class Printer {
public:
    void print() {
        std::cout << "Generic printer";
    }
};

template<>
class Printer<bool> {
public:
    void print() {
        std::cout << "Boolean printer";
    }
};

Templates in the STL

The Standard Template Library heavily depends on templates. Common containers are all generic:

  • std::vector<T>
  • std::map<Key, Value>
  • std::set<T>
  • std::array<T, N>

This allows the STL to work with virtually any user-defined or built-in type.

🧠 Important Insight
Templates are a compile-time feature. This means generic code often has zero runtime overhead compared to manually written type-specific code.

Common Template Errors

  • Forgetting to place template definitions in header files
  • Using unsupported operations for a generic type
  • Creating extremely complex compiler error messages
  • Mixing incompatible template parameter types
🎯 Exercise 8.1

Write a generic Stack<T> class with push, pop and top operations. Then specialise it for bool so that it stores bits compactly (you can use std::vector<bool> for simplicity).

🎯 Exercise 8.2

Create a function template called maxValue that returns the largest of two values. Test it using integers, floating-point numbers, and strings.

🎯 Exercise 8.3

Create a class template called Pair<T1, T2> that stores two values of different types and displays them using a member function.

Lecture 09 · Advanced

Modern C++ (C++11 +)

Intermediate ~60 min Requires: Lecture 08

Introduction to Modern C++

C++11 introduced one of the largest upgrades in the history of the language. Since then, newer standards like C++14, C++17, and C++20 have continued improving performance, readability, and developer productivity.

Modern C++ focuses on safer memory management, cleaner syntax, stronger type safety, and high-performance abstractions without sacrificing low-level control.

💡 Why Modern C++ Matters
Modern C++ makes code easier to write, safer to maintain, and less error-prone compared to older C++98-style programming.

Auto type deduction

The auto keyword allows the compiler to automatically determine a variable's type from its initializer.

auto x = 42;                // int
auto y = 3.14;               // double
auto z = std::vector<int>{1,2,3}; // deduced type

This reduces verbosity and makes code easier to read, especially when working with complex template types.

✅ Best Practice
Use auto when the type is obvious or excessively long, but avoid overusing it when it makes code harder to understand.

Type Inference with References

auto can also work with references and const qualifiers.

AutoReference.cpp
int value = 10;

auto a = value;        // copy
auto& b = value;       // reference

b = 20;

std::cout << value;    // prints 20

Using references with auto prevents unnecessary copying and improves performance.

Range-based for loop

Range-based loops simplify iteration over containers and arrays.

std::vector<int> v = {1,2,3};

for (auto& n : v) {
    n *= 2;
}

This loop automatically iterates through every element in the container.

  • auto n → creates a copy of each element
  • auto& n → references the original element
  • const auto& n → prevents modification

Initializer Lists

Modern C++ introduced uniform initialization syntax using curly braces.

InitializerList.cpp
int x{10};
std::vector<int> numbers{1, 2, 3, 4};

Brace initialization helps avoid certain narrowing conversion bugs and creates more consistent syntax across the language.

Smart pointers

One of the biggest improvements in modern C++ is automatic memory management using smart pointers.

SmartPtrDemo.cpp
#include <memory>
#include <iostream>

struct Node {
    int value;
    std::unique_ptr<Node> next;

    Node(int v) : value(v) {}
};

int main() {
    auto head = std::make_unique<Node>(1);
    head->next = std::make_unique<Node>(2);

    std::cout << head->value
              << ", "
              << head->next->value
              << std::endl;
}

std::unique_ptr automatically deletes the object when it goes out of scope, preventing memory leaks.

Types of Smart Pointers

  • std::unique_ptr → exclusive ownership
  • std::shared_ptr → shared ownership using reference counting
  • std::weak_ptr → non-owning reference used with shared pointers
🧠 Important Insight
Smart pointers are a core part of RAII (Resource Acquisition Is Initialization), a major C++ design principle for automatic resource management.

Move semantics

Move semantics allow resources to be transferred instead of copied, dramatically improving performance when working with large objects.

MoveDemo.cpp
std::vector<int> data = {1,2,3,4};

std::vector<int> movedData = std::move(data);

After the move operation, ownership of the internal resources transfers to movedData.

Lambda expressions

Lambdas are anonymous inline functions introduced in C++11. They are commonly used with STL algorithms.

auto add = [](int a, int b) {
    return a + b;
};

std::cout << add(3,4) << '\n';

The syntax may look unusual at first:

  • [] → capture list
  • (int a, int b) → parameters
  • { } → function body

Lambda Capture

Lambdas can capture variables from the surrounding scope.

LambdaCapture.cpp
int multiplier = 5;

auto multiply = [multiplier](int x) {
    return x * multiplier;
};

std::cout << multiply(3);

Capturing variables allows lambdas to behave similarly to lightweight function objects.

constexpr

constexpr enables compile-time constants and functions.

constexpr int square(int x) {
    return x*x;
}

static_assert(square(5) == 25);

If the compiler can evaluate the expression during compilation, it will do so automatically.

nullptr

Modern C++ introduced nullptr to replace the older NULL macro.

Nullptr.cpp
int* ptr = nullptr;

if(ptr == nullptr) {
    std::cout << "Pointer is null";
}

nullptr provides better type safety compared to the older integer-based NULL value.

Strongly Typed Enums

C++11 introduced scoped enumerations using enum class.

EnumClass.cpp
enum class Color {
    Red,
    Green,
    Blue
};

Color c = Color::Red;

Unlike traditional enums, scoped enums prevent accidental implicit conversions.

Using STL Algorithms with Lambdas

Modern C++ works extremely well with STL algorithms.

SortLambda.cpp
std::vector<int> values = {5,2,8,1};

std::sort(values.begin(), values.end(),
    [](int a, int b) {
        return a > b;
    });

This sorts the vector in descending order using a lambda comparator.

Benefits of Modern C++

  • Cleaner and shorter syntax
  • Safer memory management
  • Improved performance through move semantics
  • Powerful generic programming support
  • Better compatibility with STL algorithms

Common Beginner Mistakes

  • Overusing auto when types become unclear
  • Confusing copies with references in range-based loops
  • Incorrect lambda capture usage
  • Using raw pointers instead of smart pointers
  • Forgetting that moved-from objects should not be relied upon
🎯 Exercise 9.1

Write a program that uses a std::vector<int> of random numbers, then sorts them with std::sort using a lambda comparator that orders them in descending order.

🎯 Exercise 9.2

Create a lambda function that multiplies two integers and stores it in a variable using auto. Call the lambda with different values.

🎯 Exercise 9.3

Create a std::unique_ptr to a dynamically allocated object and demonstrate automatic cleanup when it goes out of scope.

Lecture 10 · Advanced

Exception Handling

Intermediate ~45 min Requires: Lecture 09

try / catch / finally

C++ has try, catch and throw. The finally pattern is emulated with RAII.

ExceptionDemo.cpp
#include <stdexcept>
#include <iostream>

double divide(double a, double b) {
    if (b == 0.0) throw std::runtime_error("Division by zero");
    return a / b;
}

int main() {
    try {
        std::cout << divide(10, 2) << std::endl;
        std::cout << divide(5, 0) << std::endl;
    } catch (const std::runtime_error& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
}

Custom exception types

class InvalidAgeException : public std::runtime_error {
public:
    InvalidAgeException(const std::string& msg) : std::runtime_error(msg) {}
};
🎯 Exercise 10.1

Implement a BankAccount class with deposit, withdraw and balance. Throw a custom InsufficientFundsException when a withdrawal would result in a negative balance and handle it in main.

Lecture 11 · Advanced

Concurrency

Intermediate ~60 min Requires: Lecture 10

Thread basics

C++11 introduced std::thread, std::mutex and related primitives.

ThreadDemo.cpp
#include <thread>
#include <iostream>

void worker(int id) {
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread " << id << ": " << i << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
    }
}

int main() {
    std::thread t1(worker, 1);
    std::thread t2(worker, 2);
    t1.join();
    t2.join();
}

Mutex & lock_guard

std::mutex m;
{
    std::lock_guard<std::mutex> lock(m);
    // critical section
}

Thread pool (high‑level)

Standard C++ does not ship a thread pool, but you can build one with std::async or use a third‑party library.

AsyncDemo.cpp
#include <future>
#include <iostream>

int fib(int n) {
    if (n <= 1) return n;
    return fib(n-1) + fib(n-2);
}

int main() {
    auto fut = std::async(std::launch::async, fib, 30);
    std::cout << "Computing…" << std::endl;
    int result = fut.get();               // blocks until fib finishes
    std::cout << "Result = " << result << std::endl;
}
🎯 Exercise 11.1

Write a producer‑consumer example using a std::queue protected by a std::mutex and a std::condition_variable. One thread should push numbers 1‑100 into the queue, the other should pop and print them.

Lecture 12 · Capstone

Capstone Project – Console To‑Do List

Advanced ~90 min Requires: All Lectures

Implement a **single‑file** C++ console program that lets the user manage a simple To‑Do list.

Feature checklist

  • Store tasks as objects (struct Task { int id; std::string text; bool done; }).
  • Add, toggle (complete/incomplete), delete, and list tasks.
  • Persist the list to a text file (tasks.txt) on exit and load it on start.
  • Use std::vector, std::fstream, std::getline, and basic RAII.
  • Handle I/O errors with exceptions.
  • Optional: colour the “done” tasks using ANSI escape codes.

Starter skeleton

TodoApp.cpp
#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
#include <string>

struct Task {
    int id;
    std::string text;
    bool done;

    Task(int i, std::string t)
        : id(i), text(std::move(t)), done(false) {}

    std::string serialize() const {
        return std::to_string(id) + "," + text + "," + (done ? "1" : "0");
    }

    static Task deserialize(const std::string& line) {
        std::istringstream ss(line);
        std::string part;
        int i;
        std::getline(ss, part, ',');
        i = std::stoi(part);
        std::getline(ss, part, ',');
        std::string txt = part;
        std::getline(ss, part, ',');
        bool d = (part == "1");
        Task t(i, txt);
        t.done = d;
        return t;
    }
};

class TodoApp {
    static constexpr const char* FILE_NAME = "tasks.txt";
    std::vector<Task> tasks;
    int nextId = 1;

    void load() {
        std::ifstream in(FILE_NAME);
        if (!in) return;
        std::string line;
        while (std::getline(in, line)) {
            Task t = Task::deserialize(line);
            tasks.push_back(t);
            if (t.id >= nextId) nextId = t.id + 1;
        }
    }

    void save() {
        std::ofstream out(FILE_NAME, std::ios::trunc);
        for (const auto& t : tasks) {
            out << t.serialize() << \n;
        }
    }

    void printMenu() {
        std::cout << "\n--- To‑Do List ---\n";
        for (const auto& t : tasks) {
            std::cout << t.id << ". ["
                      << (t.done ? "x" : " ") << "] " << t.text << \n;
        }
        std::cout << "\n(a)dd, (t)oggle, (d)elete, (q)uit: ";
    }

    void addTask(const std::string& txt) {
        tasks.push_back(Task(nextId++, txt));
    }

    void toggleTask(int id) {
        for (auto& t : tasks) {
            if (t.id == id) { t.done = !t.done; break; }
        }
    }

    void deleteTask(int id) {
        tasks.erase(std::remove_if(tasks.begin(), tasks.end(),
                           [id](const Task& t){ return t.id == id; }),
                     tasks.end());
    }

public:
    TodoApp() { load(); }

    ~TodoApp() { save(); }

    void run() {
        while (true) {
            printMenu();
            std::string cmd;
            std::getline(std::cin, cmd);
            if (cmd.empty()) continue;
            char c = std::tolower(cmd[0]);
            if (c == 'q') break;

            switch (c) {
                case 'a':
                    std::cout << "Task description: ";
                    std::getline(std::cin, cmd);
                    addTask(cmd);
                    break;
                case 't':
                    std::cout << "ID to toggle: ";
                    std::getline(std::cin, cmd);
                    toggleTask(std::stoi(cmd));
                    break;
                case 'd':
                    std::cout << "ID to delete: ";
                    std::getline(std::cin, cmd);
                    deleteTask(std::stoi(cmd));
                    break;
                default:
                    std::cout << "Unknown command\n";
            }
        }
    }
};

int main() {
    TodoApp app;
    app.run();
    return 0;
}

What you’ll learn

  • Classes, structs and encapsulation.
  • Standard containers (vector) and algorithms.
  • File I/O with fstream and error handling.
  • Basic interactive console UI.
  • RAII – resources are automatically released.
🚀 Next steps

After this capstone you’ll be ready to move on to:

  • GUI programming (Qt, wxWidgets, SFML).
  • Modern C++ libraries (Boost, fmt, ranges).
  • Build systems (CMake, Meson).
  • Testing frameworks (GoogleTest, Catch2).
🎯 Final Challenge

Extend the app with a priority field (enum LOW/MEDIUM/HIGH) and allow sorting by priority. Add colour output (ANSI escape codes) so completed tasks appear in grey.

Lecture 13 · Advanced

Smart Pointers

Intermediate ~55 min Requires: Lecture 12

Content coming soon...

Lecture 14 · Advanced

Move Semantics & Rvalue References

Intermediate ~50 min Requires: Lecture 13

Content coming soon...

Lecture 15 · Advanced

Lambda Expressions

Intermediate ~45 min Requires: Lecture 14

Content coming soon...

Lecture 16 · Advanced

File I/O & Streams

Intermediate ~50 min Requires: Lecture 15

Content coming soon...

Lecture 17 · Professional

STL Algorithms

Advanced ~55 min Requires: Lecture 16

Content coming soon...

Lecture 18 · Professional

Design Patterns in C++

Advanced ~55 min Requires: Lecture 17

Content coming soon...

Lecture 19 · Professional

Build Systems & CMake

Advanced ~50 min Requires: Lecture 18

Content coming soon...

Lecture 20 · Professional

Final Project — C++ Application

Advanced ~90 min Requires: All Previous

Content coming soon...