DOCODIVE
Beginner Free Learning Path

C & C++ Beginner Guide

Choose your language to start learning. Switch between C and C++ anytime using the buttons below.

6–8 weeks 39 lessons 20 C 19 C++ C Projects C++ Projects C Capstone C++ Capstone No experience required

Part 1: C Language

01

What is C & Why It Matters

C is a compiled, procedural language and the foundation of modern programming. It gives low-level control over memory while staying portable across platforms. Operating systems, embedded systems, and databases are written in C.

C
#include <stdio.h>

int main() {
    printf("Hello from C!");
    return 0;
}
#include <stdio.h> imports standard input/output functions. main() is the entry point where execution starts. printf() prints text to the console. return 0 signals successful exit. Every statement ends with a semicolon, and curly braces define blocks.
terminal
Hello from C!
When compiled and run, main() executes printf(), printing the text exactly as written. return 0 tells the operating system the program completed successfully.
02

Setting Up: Compiler, IDE & Hello World

To write C you need a compiler like GCC. You write a .c source file, then compile it to an executable. The smallest runnable program is a main() function with a return statement. This 'Hello World' is the classic first step.

C
#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}
#include <stdio.h> gives access to printf. main() is the mandatory entry point. printf prints the message, and \n adds a newline so the output ends on its own line. return 0 indicates success.
terminal
Hello, World!
After compiling with 'gcc hello.c -o hello' and running './hello', the program prints the message followed by a newline. This is the standard first C program.
03

Variables & Data Types

C is statically typed — every variable must declare its type. The basic types are int (whole numbers), float (single-precision decimal), double (double-precision decimal), and char (single character). Variables store values in memory.

C
#include <stdio.h>

int main() {
    int age = 25;
    float price = 19.99;
    char grade = 'A';
    double big = 123456.789;

    printf("Age: %d\n", age);
    printf("Price: %.2f\n", price);
    printf("Grade: %c\n", grade);
    printf("Big: %.3f\n", big);
    return 0;
}
int stores a whole number, float and double store decimals, char stores one character in single quotes. printf format specifiers (%d for int, %f for float/double, %c for char) tell printf how to display each value. %.2f rounds to two decimal places.
terminal
Age: 25 Price: 19.99 Grade: A Big: 123456.789
Each printf line used the correct format specifier for its variable type. %.2f showed two decimals, %.3f showed three. This is how C prints typed data.
04

Constants (#define & const)

Constants are values that cannot change. #define creates a preprocessor macro that replaces text before compilation. The const keyword creates a typed, read-only variable. Both prevent accidental modification.

C
#include <stdio.h>

#define PI 3.14159

int main() {
    const int MAX_SCORE = 100;

    printf("PI: %.5f\n", PI);
    printf("Max score: %d\n", MAX_SCORE);
    return 0;
}
#define PI 3.14159 creates a macro — the preprocessor replaces every PI with 3.14159 before compiling. const int MAX_SCORE creates a typed constant. Both are then printed with their format specifiers.
terminal
PI: 3.14159 Max score: 100
Both constants printed their stored values. #define is text substitution, while const creates a real typed variable. Both work for fixed values.
05

Operators & Expressions

C operators perform calculations and comparisons. Arithmetic operators (+, -, *, /, %) do math. Relational operators (==, !=, >, <) compare and return true or false. Logical operators (&&, ||, !) combine conditions.

C
#include <stdio.h>

int main() {
    int a = 10, b = 3;

    printf("%d\n", a + b);
    printf("%d\n", a / b);
    printf("%d\n", a %% b);
    printf("%d\n", a > b);
    printf("%d\n", a == 10 && b == 3);
    return 0;
}
a + b adds (13). a / b does integer division (3, decimal dropped). a %% b uses modulo for remainder (1). a > b compares (1 means true). a == 10 && b == 3 checks both with logical AND (1 = true).
terminal
13 3 1 1 1
Integer division truncates the decimal. Modulo returns the remainder. In C, relational and logical expressions return 1 for true and 0 for false, which is why the comparisons print 1.
06

Input & Output (printf/scanf)

printf displays output, and scanf reads input. scanf uses format specifiers and the address-of operator (&) to store input into variables. Together they make interactive programs.

C
#include <stdio.h>

int main() {
    int age;
    char name[50];

    printf("Enter your name: ");
    scanf("%s", name);

    printf("Enter your age: ");
    scanf("%d", &age);

    printf("Hello %s, you are %d years old.\n", name, age);
    return 0;
}
scanf("%s", name) reads a string into the name array (no & needed because arrays decay to pointers). scanf("%d", &age) reads an integer into age using & to get its address. printf then prints both using %s and %d.
terminal
Enter your name: Ali Enter your age: 25 Hello Ali, you are 25 years old.
scanf paused for input, stored the typed values, and printf printed them back. Using & with age gives scanf the memory address where it should write the input.
07

Conditionals (if/else/switch)

if and else make decisions based on conditions. switch compares one value against multiple cases. Both control which code block executes. Braces group multiple statements in a block.

C
#include <stdio.h>

int main() {
    int score = 85;

    if (score >= 90) {
        printf("A\n");
    } else if (score >= 70) {
        printf("B\n");
    } else {
        printf("C\n");
    }
    return 0;
}
The first if checks score >= 90 (false). The else if checks score >= 70 (true), so 'B' prints. Because a match was found, the final else block is skipped. Only one branch runs.
terminal
B
Only the first matching block runs. 85 is not high enough for A but is high enough for B, so B printed. This is the core of decision-making in C.
08

Loops (for/while/do-while)

Loops repeat code. for is used when the iteration count is known. while repeats while a condition holds. do-while always runs at least once because the condition is checked after the body.

C
#include <stdio.h>

int main() {
    for (int i = 1; i <= 5; i++) {
        printf("%d\n", i);
    }
    return 0;
}
The for loop has three parts: int i = 1 starts the counter, i <= 5 is the condition that keeps it running, and i++ increments after each iteration. printf prints the current value each time.
terminal
1 2 3 4 5
The loop ran five times, printing 1 through 5. When i became 6, the condition i <= 5 was false, and the loop stopped.
09

Arrays

An array stores multiple values of the same type in contiguous memory. Arrays have a fixed size declared at creation, and elements are accessed by zero-based index. The size is the count of elements.

C
#include <stdio.h>

int main() {
    int nums[5] = {10, 20, 30, 40, 50};

    printf("First: %d\n", nums[0]);
    printf("Last: %d\n", nums[4]);

    int sum = 0;
    for (int i = 0; i < 5; i++) {
        sum += nums[i];
    }
    printf("Sum: %d\n", sum);
    return 0;
}
int nums[5] declares an array of 5 integers with initial values. nums[0] accesses the first, nums[4] the last. The for loop iterates index 0 to 4, adding each element to sum. Then sum is printed.
terminal
First: 10 Last: 50 Sum: 150
The array held five values. Indexing started at 0 (first = 10) and ended at 4 (last = 50). The loop summed all elements to 150.
10

Strings (char arrays)

C has no built-in string type — strings are char arrays ending with a null terminator (\0). The string.h library provides functions like strlen, strcpy, and strcmp to work with strings.

C
#include <stdio.h>
#include <string.h>

int main() {
    char name[20] = "Ali";

    printf("Name: %s\n", name);
    printf("Length: %lu\n", strlen(name));

    strcat(name, " Khan");
    printf("Full: %s\n", name);
    return 0;
}
char name[20] creates a 20-byte buffer holding 'Ali' plus the null terminator. strlen returns 3. strcat appends ' Khan' to the existing string, making 'Ali Khan'. Both results are printed.
terminal
Name: Ali Length: 3 Full: Ali Khan
strlen counted the characters before the null terminator (3). strcat concatenated the two strings. The char array held the final full name.
11

Pointers Basics

A pointer stores a memory address. The & operator gets an address, and * dereferences a pointer to access the value at that address. Pointers give C its low-level power and enable pass-by-reference.

C
#include <stdio.h>

int main() {
    int num = 42;
    int *ptr = &num;

    printf("Value: %d\n", num);
    printf("Address: %p\n", (void*)ptr);
    printf("Dereferenced: %d\n", *ptr);

    *ptr = 99;
    printf("After change: %d\n", num);
    return 0;
}
int *ptr declares a pointer. ptr = &num stores num's address. *ptr dereferences to read the value (42). *ptr = 99 writes 99 through the pointer, which changes num because both refer to the same memory.
terminal
Value: 42 Address: 0x7ffd... Dereferenced: 42 After change: 99
The pointer read and modified num directly through its address. After *ptr = 99, num became 99 because the pointer and variable share the same memory location.
12

Functions

Functions are reusable blocks of code. A function has a return type, a name, and optional parameters. void means it returns nothing. Functions break programs into manageable pieces.

C
#include <stdio.h>

void greet(char name[]) {
    printf("Hello, %s\n", name);
}

int main() {
    greet("Ali");
    greet("Sara");
    return 0;
}
greet is a function taking a char array and returning void. It prints a greeting using the parameter. main calls greet twice with different names. The function body is reused for both calls.
terminal
Hello, Ali Hello, Sara
Each call to greet printed a greeting with the passed name. Reusing one function for two inputs shows why functions make code modular.
13

Function Parameters & Return

Parameters pass data into a function. The return statement sends a value back to the caller. The return type must match the returned value's type. void means no return value.

C
#include <stdio.h>

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

int main() {
    int result = add(5, 3);
    printf("Result: %d\n", result);
    return 0;
}
add takes two int parameters and returns int. return a + b sends the sum back. In main, add(5, 3) evaluates to 8, which is stored in result and printed. The function does not print — it only returns.
terminal
Result: 8
The function computed 5 + 3 and returned 8. The caller stored the returned value. This is how functions produce values for the rest of the program.
14

Scope & Lifetime

Scope determines where a variable is visible. Variables declared inside a function are local to it. Global variables are visible everywhere. Static local variables keep their value between calls.

C
#include <stdio.h>

int global = 10;

void demo() {
    int local = 20;
    static int persistent = 0;
    persistent++;
    printf("Global: %d, Local: %d, Persistent: %d\n", global, local, persistent);
}

int main() {
    demo();
    demo();
    return 0;
}
global is visible everywhere. local is created fresh each call and destroyed after. persistent is static, so it keeps its value between calls. The function prints all three each time it runs.
terminal
Global: 10, Local: 20, Persistent: 1 Global: 10, Local: 20, Persistent: 2
global stayed 10, local reset to 20 each call, and persistent incremented from 1 to 2 because static preserved its value across calls.
15

Structs

A struct groups related variables of different types into one unit. struct defines the blueprint, and you access members using the dot operator. Structs are the foundation of data modeling in C.

C
#include <stdio.h>

struct Student {
    char name[50];
    int age;
    float grade;
};

int main() {
    struct Student s1 = {"Ali", 25, 85.5};

    printf("Name: %s\n", s1.name);
    printf("Age: %d\n", s1.age);
    printf("Grade: %.1f\n", s1.grade);
    return 0;
}
struct Student defines a blueprint with name, age, and grade. struct Student s1 creates a variable and initializes all three members. The dot operator (s1.name, s1.age, s1.grade) accesses each member.
terminal
Name: Ali Age: 25 Grade: 85.5
The struct held three related values in one unit. Dot access retrieved each member. Structs bundle data that belongs together.
16

Typedef

typedef creates an alias for an existing type. It makes code more readable and portable. A common use is creating short names for struct types.

C
#include <stdio.h>

typedef struct {
    int x;
    int y;
} Point;

int main() {
    Point p = {3, 4};
    printf("Point: (%d, %d)\n", p.x, p.y);
    return 0;
}
typedef struct {...} Point creates an alias 'Point' for the struct type, removing the need to write 'struct' each time. Point p creates a variable with x=3, y=4. p.x and p.y access the members.
terminal
Point: (3, 4)
typedef shortened the type name, so 'Point p' works instead of 'struct Point p'. The members printed their stored coordinates.
17

Enums

An enum defines a set of named integer constants. By default the first is 0, and each subsequent increments by 1. Enums make code more readable than raw numbers.

C
#include <stdio.h>

enum Day {
    MONDAY, TUESDAY, WEDNESDAY
};

int main() {
    enum Day today = WEDNESDAY;
    printf("Day value: %d\n", today);
    return 0;
}
enum Day defines three constants: MONDAY (0), TUESDAY (1), WEDNESDAY (2). enum Day today creates a variable set to WEDNESDAY. printf prints its integer value, which is 2.
terminal
Day value: 2
WEDNESDAY is the third constant, so it has value 2. Enums assign sequential integers starting from 0 by default.
18

Preprocessor Directives

The preprocessor runs before compilation. #include imports files, #define creates macros, and #ifdef conditionally compiles code. Preprocessor directives begin with # and do not end with semicolons.

C
#include <stdio.h>

#define MAX 10

int main() {
    #ifdef MAX
        printf("MAX is defined: %d\n", MAX);
    #else
        printf("MAX is not defined\n");
    #endif
    return 0;
}
#define MAX 10 creates a macro. #ifdef MAX checks if MAX is defined — since it is, the first printf compiles and the else branch is removed before compilation. #endif ends the conditional block.
terminal
MAX is defined: 10
The preprocessor kept the #ifdef branch because MAX was defined and discarded the #else branch. This is conditional compilation.
19

Dynamic Memory (malloc/free)

malloc allocates memory at runtime from the heap, returning a pointer. free releases that memory. Dynamic memory lets you work with data sizes known only at runtime. Always pair malloc with free to avoid leaks.

C
#include <stdio.h>
#include <stdlib.h>

int main() {
    int n = 3;
    int *arr = (int*)malloc(n * sizeof(int));

    arr[0] = 10;
    arr[1] = 20;
    arr[2] = 30;

    for (int i = 0; i < n; i++) {
        printf("%d\n", arr[i]);
    }

    free(arr);
    return 0;
}
malloc(n * sizeof(int)) allocates memory for 3 integers and returns a pointer cast to int*. The pointer can be used like an array. The loop prints each value. free(arr) releases the allocated memory.
terminal
10 20 30
Memory was allocated at runtime, used as an array, and then freed. malloc allowed the size to be decided by the variable n instead of a fixed compile-time constant.
20

C Beginner Recap + Mini Projects

You now know variables, constants, operators, input/output, conditionals, loops, arrays, strings, pointers, functions, scope, structs, typedef, enums, the preprocessor, and dynamic memory. Apply them in three mini projects: (1) Even/Odd Checker, (2) Simple Calculator, (3) Factorial Calculator.

C
#include <stdio.h>

int main() {
    int n;

    printf("Enter a number: ");
    scanf("%d", &n);

    if (n % 2 == 0) {
        printf("%d is even\n", n);
    } else {
        printf("%d is odd\n", n);
    }
    return 0;
}
scanf reads an integer into n. The modulo operator % checks if n is divisible by 2 — remainder 0 means even. The if/else prints the appropriate message. This is the Even/Odd Checker project.
terminal
Enter a number: 7 7 is odd
7 divided by 2 leaves remainder 1, so the else branch ran and printed '7 is odd'. This combines input, operators, and conditionals.

C Beginner Projects

About this project

Build a simple calculator that takes two numbers and an operator, then prints the result using switch.

What you'll practice
scanf/printfswitchoperatorsif/else
C
#include <stdio.h>

int main() {
    double a, b;
    char op;

    printf("Enter first number: ");
    scanf("%lf", &a);

    printf("Enter second number: ");
    scanf("%lf", &b);

    printf("Enter operator (+, -, *, /): ");
    scanf(" %c", &op);

    switch (op) {
        case '+': printf("%.2f + %.2f = %.2f\n", a, b, a + b); break;
        case '-': printf("%.2f - %.2f = %.2f\n", a, b, a - b); break;
        case '*': printf("%.2f * %.2f = %.2f\n", a, b, a * b); break;
        case '/':
            if (b != 0) printf("%.2f / %.2f = %.2f\n", a, b, a / b);
            else printf("Error: Cannot divide by zero.\n");
            break;
        default: printf("Invalid operator.\n");
    }
    return 0;
}
Sample Run
terminal
Enter first number: 10 Enter second number: 4 Enter operator (+, -, *, /): / 10.00 / 4.00 = 2.50

About this project

Compute factorial and the first N Fibonacci numbers using loops and functions.

What you'll practice
functionsloopsreturnscanf
C
#include <stdio.h>

int factorial(int n) {
    int result = 1;
    for (int i = 1; i <= n; i++) result *= i;
    return result;
}

void fibonacci(int n) {
    int a = 0, b = 1, next;
    for (int i = 0; i < n; i++) {
        printf("%d ", a);
        next = a + b;
        a = b;
        b = next;
    }
    printf("\n");
}

int main() {
    printf("Factorial of 5: %d\n", factorial(5));
    printf("First 7 Fibonacci numbers: ");
    fibonacci(7);
    return 0;
}
Sample Run
terminal
Factorial of 5: 120 First 7 Fibonacci numbers: 0 1 1 2 3 5 8

About this project

Find the minimum, maximum, and average of an array of numbers.

What you'll practice
arraysloopsif/else
C
#include <stdio.h>

int main() {
    int nums[] = {12, 5, 18, 3, 9};
    int size = 5;
    int min = nums[0], max = nums[0], sum = 0;

    for (int i = 0; i < size; i++) {
        sum += nums[i];
        if (nums[i] < min) min = nums[i];
        if (nums[i] > max) max = nums[i];
    }

    double avg = (double)sum / size;
    printf("Min: %d\nMax: %d\nAverage: %.2f\n", min, max, avg);
    return 0;
}
Sample Run
terminal
Min: 3 Max: 18 Average: 9.40

C Capstone Project

About this project

Store and display multiple student records using structs and arrays.

What you'll practice
structsarraysloopsscanf/printf
C
#include <stdio.h>

struct Student {
    char name[50];
    int age;
    float grade;
};

int main() {
    struct Student students[3];

    for (int i = 0; i < 3; i++) {
        printf("Student %d name: ", i + 1);
        scanf("%s", students[i].name);
        printf("Age: ");
        scanf("%d", &students[i].age);
        printf("Grade: ");
        scanf("%f", &students[i].grade);
    }

    printf("\n--- Records ---\n");
    for (int i = 0; i < 3; i++) {
        printf("%s | Age: %d | Grade: %.1f\n",
               students[i].name, students[i].age, students[i].grade);
    }
    return 0;
}
Sample Run
terminal
Student 1 name: Ali Age: 25 Grade: 85.5 Student 2 name: Sara Age: 22 Grade: 92.0 Student 3 name: Zain Age: 28 Grade: 78.5 --- Records --- Ali | Age: 25 | Grade: 85.5 Sara | Age: 22 | Grade: 92.0 Zain | Age: 28 | Grade: 78.5

Part 2: C++ Language

01

What is C++ & Why It Matters

C++ extends C with classes, objects, and modern features while keeping low-level control. It supports multiple programming styles: procedural, object-oriented, and generic. Games, browsers, and performance-critical software use C++.

C++
#include <iostream>

int main() {
    std::cout << "Hello from C++!";
    return 0;
}
#include <iostream> imports input/output streams. main() is the entry point. std::cout sends text to standard output, and << inserts the string. return 0 signals success. The std:: prefix shows cout lives in the standard namespace.
terminal
Hello from C++!
main() ran std::cout, which printed the string. The << operator streamed text to output. This is the standard first C++ program.
02

Namespace & std::

A namespace groups related names and avoids collisions. The standard library lives in the std namespace. You can write std::cout each time, or add 'using namespace std;' to bring all standard names into scope.

C++
#include <iostream>

int main() {
    using namespace std;
    cout << "No std:: prefix needed" << endl;
    return 0;
}
using namespace std imports all standard names, so cout and endl can be used without the std:: prefix. endl prints a newline and flushes the output. This is shorter but can cause name collisions in large projects.
terminal
No std:: prefix needed
cout printed the string and endl added a newline. The using directive removed the need for std:: prefixes, making the code shorter.
03

Input & Output (cin/cout)

C++ uses cin for input and cout for output. The extraction operator >> reads data from cin, and the insertion operator << writes to cout. Together they handle console interaction cleanly.

C++
#include <iostream>
#include <string>

int main() {
    std::string name;
    int age;

    std::cout << "Enter your name: ";
    std::cin >> name;

    std::cout << "Enter your age: ";
    std::cin >> age;

    std::cout << "Hello " << name << ", you are " << age << " years old." << std::endl;
    return 0;
}
cin >> name reads a string, and cin >> age reads an integer. The >> operator automatically handles type conversion. cout chains multiple insertions with <<. endl ends the line.
terminal
Enter your name: Ali Enter your age: 25 Hello Ali, you are 25 years old.
cin stored the typed values, and cout printed them in a chained statement. The extraction operator made type-safe input simple.
04

The string Class

C++ has a built-in std::string class that manages text automatically. Unlike C char arrays, strings grow and shrink dynamically and support methods like length(), append(), and comparison with ==.

C++
#include <iostream>
#include <string>

int main() {
    std::string greeting = "Hello";
    greeting += " World";

    std::cout << "Text: " << greeting << std::endl;
    std::cout << "Length: " << greeting.length() << std::endl;
    std::cout << "Substring: " << greeting.substr(0, 5) << std::endl;
    return 0;
}
std::string creates a dynamic string. += appends text. length() returns the character count. substr(0, 5) returns characters from index 0 up to (not including) 5, which is 'Hello'.
terminal
Text: Hello World Length: 11 Substring: Hello
The string grew via +=, length() counted 11 characters including the space, and substr() extracted 'Hello'. std::string removes all manual memory management.
05

References

A reference is an alias for an existing variable. It is declared with & and must be initialized. References provide pass-by-reference to functions, letting them modify the original variable without pointers.

C++
#include <iostream>

void addFive(int &num) {
    num += 5;
}

int main() {
    int value = 10;
    addFive(value);
    std::cout << "Value: " << value << std::endl;
    return 0;
}
int &num is a reference parameter — it aliases the variable passed in. num += 5 modifies the original 'value' directly. No pointer or address-of is needed, and no copy is made.
terminal
Value: 15
value changed from 10 to 15 because the function modified it through the reference. References make pass-by-reference clean and safe.
06

Vectors (Dynamic Arrays)

std::vector is a resizable array that manages its own memory. Unlike C arrays, vectors can grow with push_back and know their size with size(). They are the default choice for dynamic lists.

C++
#include <iostream>
#include <vector>

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

    std::cout << "Size: " << nums.size() << std::endl;
    std::cout << "First: " << nums[0] << std::endl;
    std::cout << "Last: " << nums[nums.size() - 1] << std::endl;
    return 0;
}
std::vector<int> creates a dynamic integer array initialized with three values. push_back(4) appends to the end. size() returns the current count (4). Indexing works like a regular array.
terminal
Size: 4 First: 1 Last: 4
The vector grew from 3 to 4 elements. Index 0 held the first, and index 3 held the last. Vectors handle growth and sizing automatically.
07

Range-Based for Loop

The range-based for loop iterates over containers without an index. It is cleaner and safer than traditional for loops when you need to visit every element of an array, vector, or string.

C++
#include <iostream>
#include <vector>

int main() {
    std::vector<int> nums = {10, 20, 30};

    for (int n : nums) {
        std::cout << n << std::endl;
    }
    return 0;
}
for (int n : nums) iterates over every element in nums, copying each into n for that iteration. The loop body prints n. No index, no bounds check, no manual increment — the syntax handles it.
terminal
10 20 30
Each element was visited in order and printed. The range-for eliminated boilerplate while remaining type-safe.
08

Functions & Overloading

C++ functions are reusable blocks of code. Function overloading lets you define multiple functions with the same name but different parameters. The compiler picks the right one based on arguments.

C++
#include <iostream>

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

double add(double a, double b) {
    return a + b;
}

int main() {
    std::cout << add(5, 3) << std::endl;
    std::cout << add(5.5, 3.2) << std::endl;
    return 0;
}
Two add functions exist: one for ints, one for doubles. The compiler selects the int version when both arguments are ints, and the double version when they are doubles. Overloading works because the parameter lists differ.
terminal
8 8.7
The first call matched the int version (5 + 3 = 8), and the second matched the double version (5.5 + 3.2 = 8.7). One function name handled both types.
09

Default Arguments

Default arguments provide fallback values when a parameter is omitted. They are written in the function declaration with =. Default values make functions flexible without requiring overloads.

C++
#include <iostream>

int power(int base, int exponent = 2) {
    int result = 1;
    for (int i = 0; i < exponent; i++) {
        result *= base;
    }
    return result;
}

int main() {
    std::cout << power(5) << std::endl;
    std::cout << power(5, 3) << std::endl;
    return 0;
}
power takes a base and an optional exponent with default 2. power(5) uses the default, computing 5 squared. power(5, 3) overrides it, computing 5 cubed. The loop multiplies base exponent times.
terminal
25 125
The first call used the default exponent 2 (5² = 25). The second passed 3 explicitly (5³ = 125). Default arguments made the common case concise.
10

Classes & Objects

A class bundles data (attributes) and behavior (methods) into one type. An object is an instance of a class. Classes are the core of object-oriented programming in C++.

C++
#include <iostream>
#include <string>

class Car {
public:
    std::string brand;
    int year;

    void info() {
        std::cout << brand << " " << year << std::endl;
    }
};

int main() {
    Car myCar;
    myCar.brand = "Toyota";
    myCar.year = 2020;
    myCar.info();
    return 0;
}
class Car defines a new type with public members brand and year, plus a method info(). myCar is an object of type Car. Its attributes are assigned, then info() prints them.
terminal
Toyota 2020
The object stored data and used its method to display it. Classes group state and behavior, which is the foundation of OOP.
11

Constructors

A constructor initializes an object when it is created. It has the same name as the class and no return type. Constructors can accept parameters to set initial attribute values.

C++
#include <iostream>
#include <string>

class Person {
public:
    std::string name;
    int age;

    Person(std::string n, int a) {
        name = n;
        age = a;
    }
};

int main() {
    Person p("Ali", 25);
    std::cout << p.name << " " << p.age << std::endl;
    return 0;
}
Person has a constructor taking a name and age, assigning them to the object's attributes. Person p("Ali", 25) creates an object and runs the constructor immediately.
terminal
Ali 25
The constructor set name to 'Ali' and age to 25 during creation. This is the standard way to initialize object state.
12

Access Specifiers (public/private)

Access specifiers control visibility. public members are accessible from anywhere. private members are only accessible inside the class. This is encapsulation — hiding internal data behind a controlled interface.

C++
#include <iostream>

class BankAccount {
private:
    double balance = 0;

public:
    void deposit(double amount) {
        balance += amount;
    }

    double getBalance() {
        return balance;
    }
};

int main() {
    BankAccount acc;
    acc.deposit(100);
    std::cout << acc.getBalance() << std::endl;
    return 0;
}
balance is private — inaccessible from outside the class. deposit() and getBalance() are public methods that safely change and read balance. main uses these methods instead of touching balance directly.
terminal
100
The private balance was protected. External code had to use the public interface to modify and read it. This is encapsulation in action.
13

Getters & Setters

Getters read private attributes, and setters modify them with validation. This gives controlled access to data, ensuring invalid values are rejected before they corrupt state.

C++
#include <iostream>
#include <string>

class Student {
private:
    std::string name;

public:
    std::string getName() {
        return name;
    }

    void setName(std::string n) {
        if (!n.empty()) {
            name = n;
        }
    }
};

int main() {
    Student s;
    s.setName("Ali");
    std::cout << s.getName() << std::endl;
    return 0;
}
getName() returns the private name. setName() only assigns if the input is not empty — that is validation. main uses the public methods instead of the private field directly.
terminal
Ali
setName stored 'Ali' because it passed validation. getName returned it. The private field stayed protected behind the getter/setter.
14

Inheritance

Inheritance lets a class acquire members from another class. The derived class extends the base class, reusing code and adding its own behavior. This models 'is-a' relationships.

C++
#include <iostream>
#include <string>

class Animal {
public:
    std::string name;

    void eat() {
        std::cout << name << " is eating" << std::endl;
    }
};

class Dog : public Animal {
public:
    void bark() {
        std::cout << name << " barks" << std::endl;
    }
};

int main() {
    Dog d;
    d.name = "Rex";
    d.eat();
    d.bark();
    return 0;
}
Dog : public Animal makes Dog inherit Animal's name and eat(). Dog adds its own bark() method. The Dog object can use both inherited and own members.
terminal
Rex is eating Rex barks
The inherited eat() and the new bark() both worked on the Dog object. Inheritance reused Animal's code while letting Dog extend it.
15

Function Overriding

Overriding lets a derived class redefine a base class method. The base method must be virtual, and the derived method has the same signature but different behavior. The override keyword catches mistakes.

C++
#include <iostream>

class Animal {
public:
    virtual void sound() {
        std::cout << "Some sound" << std::endl;
    }
};

class Dog : public Animal {
public:
    void sound() override {
        std::cout << "Woof!" << std::endl;
    }
};

int main() {
    Animal* a = new Dog();
    a->sound();
    return 0;
}
Animal's sound() is virtual, allowing overriding. Dog overrides sound() with its own version using the override keyword. A base-class pointer a points to a Dog object. a->sound() calls Dog's version because of polymorphism.
terminal
Woof!
Even though the pointer type is Animal, the actual object is a Dog, so Dog's sound() ran. Virtual functions enable this runtime dispatch.
16

Polymorphism

Polymorphism lets one interface work with different types. A base-class reference or pointer can point to any derived object, and virtual method calls dispatch to the correct implementation at runtime.

C++
#include <iostream>

class Shape {
public:
    virtual void draw() {
        std::cout << "Drawing shape" << std::endl;
    }
};

class Circle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing circle" << std::endl;
    }
};

class Square : public Shape {
public:
    void draw() override {
        std::cout << "Drawing square" << std::endl;
    }
};

int main() {
    Shape* s1 = new Circle();
    Shape* s2 = new Square();
    s1->draw();
    s2->draw();
    return 0;
}
Shape has a virtual draw(). Circle and Square override it. Two Shape pointers point to different derived objects. Calling draw() dispatches to the correct override at runtime.
terminal
Drawing circle Drawing square
The same draw() call produced different results based on the actual object type. This runtime polymorphism is powered by virtual functions.
17

Destructors

A destructor runs when an object is destroyed. It has the class name prefixed with ~ and no parameters. Destructors free resources like dynamic memory, closing the object's lifecycle.

C++
#include <iostream>

class Resource {
public:
    Resource() {
        std::cout << "Resource acquired" << std::endl;
    }

    ~Resource() {
        std::cout << "Resource released" << std::endl;
    }
};

int main() {
    {
        Resource r;
    }
    std::cout << "End of main" << std::endl;
    return 0;
}
The constructor prints when the object is created, and the destructor (~Resource) prints when it is destroyed. The object r exists only inside the inner block. When the block ends, the destructor runs automatically.
terminal
Resource acquired Resource released End of main
The constructor ran first, then the destructor when the object went out of scope, then main continued. This is RAII — resources are automatically cleaned up.
18

Exception Handling (try/catch)

C++ handles errors with try/catch. Code in try is monitored, and throw raises an exception. catch handles specific types. This separates normal flow from error handling and prevents crashes.

C++
#include <iostream>
#include <stdexcept>

int main() {
    try {
        throw std::runtime_error("Something went wrong");
    } catch (const std::exception& e) {
        std::cout << "Caught: " << e.what() << std::endl;
    }
    std::cout << "Program continues" << std::endl;
    return 0;
}
throw std::runtime_error creates an exception. The catch block catches it as a std::exception reference and prints e.what(), which returns the error message. After catch, the program continues.
terminal
Caught: Something went wrong Program continues
The exception was caught, preventing a crash. The message was retrieved via what(), and execution resumed after the try/catch. This is graceful error handling.
19

C++ Beginner Recap + Mini Projects

You now know namespaces, input/output, the string class, references, vectors, range-for, overloading, default arguments, classes, constructors, access specifiers, getters/setters, inheritance, overriding, polymorphism, destructors, and exceptions. Apply them in three mini projects: (1) Simple Calculator, (2) Average Finder with vector, (3) Class-based Bank Account.

C++
#include <iostream>
#include <vector>

int main() {
    std::vector<int> scores;
    int n;

    std::cout << "How many scores? ";
    std::cin >> n;

    int sum = 0;
    for (int i = 0; i < n; i++) {
        int score;
        std::cout << "Score " << (i + 1) << ": ";
        std::cin >> score;
        scores.push_back(score);
        sum += score;
    }

    double avg = (double)sum / n;
    std::cout << "Average: " << avg << std::endl;
    return 0;
}
A vector stores scores dynamically. The loop reads each score, appends it with push_back, and adds to sum. (double)sum casts for decimal division. The average is printed.
terminal
How many scores? 3 Score 1: 85 Score 2: 90 Score 3: 78 Average: 84.3333
The vector held three scores, the sum was 253, and casting to double produced a precise average. This combines vectors, loops, and I/O — the C++ way.

C++ Beginner Projects

About this project

Build a menu-driven to-do list that can add, view, and remove tasks using std::vector.

What you'll practice
vectorpush_backswitchcin/cout
C++
#include <iostream>
#include <vector>
#include <string>

int main() {
    std::vector<std::string> tasks;
    int choice;

    do {
        std::cout << "\n1. Add task\n2. View tasks\n3. Exit\nChoose: ";
        std::cin >> choice;
        std::cin.ignore();

        switch (choice) {
            case 1: {
                std::string task;
                std::cout << "Task: ";
                std::getline(std::cin, task);
                tasks.push_back(task);
                break;
            }
            case 2:
                for (size_t i = 0; i < tasks.size(); i++) {
                    std::cout << i + 1 << ". " << tasks[i] << std::endl;
                }
                break;
        }
    } while (choice != 3);

    return 0;
}
Sample Run
terminal
1. Add task 2. View tasks 3. Exit Choose: 1 Task: Learn C++ 1. Add task 2. View tasks 3. Exit Choose: 2 1. Learn C++

About this project

Build a BankAccount class with deposit, withdraw, and getBalance using encapsulation.

What you'll practice
classesprivatemethods
C++
#include <iostream>

class BankAccount {
private:
    double balance = 0;

public:
    void deposit(double amount) {
        if (amount > 0) balance += amount;
    }

    void withdraw(double amount) {
        if (amount > 0 && amount <= balance) balance -= amount;
    }

    double getBalance() {
        return balance;
    }
};

int main() {
    BankAccount acc;
    acc.deposit(100);
    acc.withdraw(40);
    acc.withdraw(100);  // rejected (insufficient)

    std::cout << "Balance: " << acc.getBalance() << std::endl;
    return 0;
}
Sample Run
terminal
Balance: 60

About this project

Count how many times each word appears in a string using std::map.

What you'll practice
mapstringistringstream
C++
#include <iostream>
#include <map>
#include <sstream>
#include <string>

int main() {
    std::string text = "the cat and the dog";
    std::istringstream stream(text);
    std::string word;
    std::map<std::string, int> counts;

    while (stream >> word) {
        counts[word]++;
    }

    for (const auto& entry : counts) {
        std::cout << entry.first << ": " << entry.second << std::endl;
    }
    return 0;
}
Sample Run
terminal
and: 1 cat: 1 dog: 1 the: 2

C++ Capstone Project

About this project

Build a library system using an abstract base class, inheritance, and polymorphism.

What you'll practice
inheritancevirtualvectorpolymorphism
C++
#include <iostream>
#include <vector>
#include <string>

class Item {
public:
    std::string title;
    Item(std::string t) : title(t) {}
    virtual void info() = 0;
};

class Book : public Item {
public:
    std::string author;
    Book(std::string t, std::string a) : Item(t), author(a) {}
    void info() override {
        std::cout << "Book: " << title << " by " << author << std::endl;
    }
};

class Magazine : public Item {
public:
    int issue;
    Magazine(std::string t, int i) : Item(t), issue(i) {}
    void info() override {
        std::cout << "Magazine: " << title << " Issue " << issue << std::endl;
    }
};

int main() {
    std::vector<Item*> catalog;
    catalog.push_back(new Book("Effective C++", "Scott Meyers"));
    catalog.push_back(new Magazine("Tech Monthly", 42));

    for (Item* item : catalog) {
        item->info();
    }
    return 0;
}
Sample Run
terminal
Book: Effective C++ by Scott Meyers Magazine: Tech Monthly Issue 42
You've completed all 40 lessons (20 C + 20 C++). Ready to continue?

Level up with C/C++ Intermediate, Advanced, and Practice resources.

📱 Scan this QR code with your phone camera to instantly open this page.

Works on iOS, Android, and any modern device. No app installation required.

Account Verified!

Your email has been verified successfully.