• Follow Us On :
C++ Tutorial

C++ Tutorial for Beginners: The Ultimate Proven Guide to Master C++ Programming in 2026

When you use a modern video game, launch an operating system, run a high-frequency trading algorithm, or interact with any embedded device — there is a good chance the core of that software is written in C++tutorial. For over four decades, C++ has been the language of choice when performance, control, and reliability matter most. It powers everything from the Linux kernel and Windows OS components to game engines like Unreal Engine, databases like MySQL, and browsers like Chrome.

This ultimate C++ tutorial is your complete, practical roadmap to learning one of the most powerful and enduring programming languages ever created. Whether you’re a complete beginner, a Python or JavaScript developer wanting to learn systems programming, or a student preparing for competitive programming and technical interviews — this guide takes you from your very first C++ program to advanced concepts with clarity and depth.

In this comprehensive C++ tutorial, you’ll master everything: the history and fundamentals of C++, variables and data types, control flow, functions, arrays, pointers and references, object-oriented programming (OOP), inheritance and polymorphism, templates, the Standard Template Library (STL), memory management, file handling, exception handling, modern C++ features (C++11/14/17/20), real-world projects, and a clear career roadmap.

Every concept in this C++ tutorial is reinforced with real, working code examples you can compile and run immediately.

Let’s write your first C++ program.

What is C++? — The Foundation

C++ is a general-purpose, statically typed, compiled, multi-paradigm programming language created by Bjarne Stroustrup at Bell Labs starting in 1979 (originally called “C with Classes”) and formally released as C++ in 1983. It was designed as a direct extension of the C language, adding object-oriented programming features while preserving C’s low-level power and efficiency.

C++ is standardized by ISO/IEC, with major revisions released as:

  • C++98 / C++03 — Original standard
  • C++11 — Major modernization (auto, lambda, smart pointers, range-for)
  • C++14 / C++17 — Incremental improvements
  • C++20 — Concepts, ranges, coroutines, modules
  • C++23 — Latest standard

Why Learn C++ in 2025?

1. Unmatched Performance C++ compiles directly to machine code. There is no virtual machine, no garbage collector, no interpreter overhead. C++ programs execute at speeds approaching the theoretical maximum of the hardware — critical for games, real-time systems, and high-frequency trading.

2. Complete Hardware Control C++ gives you direct control over memory allocation and deallocation, CPU cache behavior, and hardware registers. No other mainstream language offers this level of control.

3. Massive Ecosystem and Legacy Trillions of lines of C++ code exist in production. Every major technology company has critical C++ infrastructure. Adobe, Google, Microsoft, EA, Epic Games, Bloomberg, and virtually every game studio hire C++ engineers.

4. Foundation for Systems Programming Understanding C++ makes you a better programmer in every other language. It teaches you what happens under the hood — memory management, data layout, compilation, linking — knowledge that pays dividends across your entire career.

5. Competitive Programming C++ is the dominant language in competitive programming (Codeforces, LeetCode, ICPC) because of its speed and the power of the STL.

C++ Use Cases

DomainExamples
Game DevelopmentUnreal Engine, Unity (core), Call of Duty, GTA
Operating SystemsWindows (core), parts of Linux/macOS
BrowsersChrome (V8 engine), Firefox (Gecko)
DatabasesMySQL, MongoDB, SQLite
Embedded SystemsAutomotive ECUs, robotics, IoT devices
High-Frequency TradingSub-microsecond trading algorithms
Scientific ComputingNASA systems, physics simulations
Compilers/ToolsLLVM, GCC, many language runtimes

Setting Up Your C++ Development Environment

Step 1: Install a Compiler

On Windows:

  • Option A (Recommended): Install Visual Studio Community (free) — includes MSVC compiler
  • Option B: Install MinGW-w64 (GCC for Windows) + VS Code

On macOS:

bash
# Install Xcode Command Line Tools (includes Clang)
xcode-select --install

# Verify installation
clang++ --version
g++ --version

On Linux (Ubuntu/Debian):

bash
sudo apt update
sudo apt install build-essential g++ gdb cmake -y
g++ --version

Step 2: Choose Your IDE or Editor

ToolBest ForCost
Visual StudioWindows, full-featured IDEFree (Community)
CLionCross-platform, JetBrainsPaid (free for students)
VS Code + C++ ExtensionLightweight, all platformsFree
Code::BlocksBeginners, lightweightFree
XcodemacOS developmentFree

Step 3: Write and Compile Your First Program

cpp
// hello.cpp — Your first C++ program

#include <iostream>  // Include standard input/output library
#include <string>    // Include string library

int main() {         // Main function — every C++ program starts here
    std::cout << "Hello, World!" << std::endl;
    std::cout << "Welcome to elearncourses.com C++ Tutorial!" << std::endl;

    // Get user input
    std::string name;
    std::cout << "Enter your name: ";
    std::getline(std::cin, name);  // Read full line including spaces

    std::cout << "Welcome, " << name << "! Let's master C++!" << std::endl;

    return 0;  // Return 0 indicates successful execution
}
 
 
bash
# Compile the program
g++ -std=c++17 -o hello hello.cpp

# Run it
./hello          # Linux/macOS
hello.exe        # Windows

# Compile with warnings and debug info (recommended during development)
g++ -std=c++17 -Wall -Wextra -g -o hello hello.cpp

Understanding the compilation process:

Source Code (.cpp) → Preprocessor → Compiler → Assembler → Linker → Executable
     hello.cpp    →   (macros)   →  (.o file) →  (assembly) → (libraries) → hello

Chapter 1: C++ Fundamentals

Variables, Data Types, and Constants

cpp
#include <iostream>
#include <string>
#include <limits>   // For numeric limits
#include <cmath>    // For math functions

int main() {

    // ── FUNDAMENTAL DATA TYPES ──────────────────────────────

    // Integer types
    int age = 25;                    // 4 bytes: -2,147,483,648 to 2,147,483,647
    short smallNum = 100;            // 2 bytes: -32,768 to 32,767
    long bigNum = 2147483648L;       // 4-8 bytes
    long long veryBigNum = 9223372036854775807LL; // 8 bytes

    // Unsigned types (no negative values — double positive range)
    unsigned int positiveOnly = 4294967295U;
    unsigned long long maxUnsigned = 18446744073709551615ULL;

    // Floating-point types
    float pi_f = 3.14159f;           // 4 bytes, ~7 decimal digits precision
    double pi_d = 3.141592653589793; // 8 bytes, ~15 decimal digits precision
    long double precise = 3.14159265358979323846L; // 16 bytes on most systems

    // Character type
    char grade = 'A';                // 1 byte: single character
    char newline = '\n';             // Escape sequence
    wchar_t wide = L'Ω';            // Wide character (Unicode)

    // Boolean type
    bool isLearning = true;          // true (1) or false (0)

    // String (not a primitive — it's a class from std library)
    std::string courseName = "C++ Tutorial";

    // ── AUTO TYPE DEDUCTION (C++11) ──────────────────────────
    auto x = 42;           // int
    auto y = 3.14;         // double
    auto z = "hello";      // const char*
    auto s = std::string("hello"); // std::string
    auto flag = true;      // bool

    // ── CONSTANTS ───────────────────────────────────────────
    const double GRAVITY = 9.80665;       // Cannot be changed after initialization
    const int MAX_STUDENTS = 50;
    constexpr double TAX_RATE = 0.18;     // Compile-time constant (C++11)
    constexpr int ARRAY_SIZE = 100;       // Must be evaluatable at compile time

    // #define (old C-style macro — avoid in modern C++)
    // #define PI 3.14159  // No type safety — avoid

    // ── PRINT DATA TYPE SIZES ────────────────────────────────
    std::cout << "=== Data Type Sizes ===" << std::endl;
    std::cout << "int:       " << sizeof(int) << " bytes" << std::endl;
    std::cout << "long long: " << sizeof(long long) << " bytes" << std::endl;
    std::cout << "float:     " << sizeof(float) << " bytes" << std::endl;
    std::cout << "double:    " << sizeof(double) << " bytes" << std::endl;
    std::cout << "char:      " << sizeof(char) << " byte" << std::endl;
    std::cout << "bool:      " << sizeof(bool) << " byte" << std::endl;

    // ── NUMERIC LIMITS ───────────────────────────────────────
    std::cout << "\nMax int: " << std::numeric_limits<int>::max() << std::endl;
    std::cout << "Min int: " << std::numeric_limits<int>::min() << std::endl;
    std::cout << "Max double: " << std::numeric_limits<double>::max() << std::endl;

    // ── TYPE CONVERSIONS ─────────────────────────────────────
    // Implicit conversion (automatic)
    int intVal = 42;
    double doubleVal = intVal;        // int → double (safe, widening)
    int truncated = 3.9;             // double → int (loses .9 — narrowing)

    // Explicit casting (recommended)
    double price = 99.99;
    int roundedPrice = static_cast<int>(price);    // Modern C++ cast
    int oldCast = (int)price;                       // C-style cast (avoid)

    std::cout << "\nPrice: " << price << std::endl;
    std::cout << "Rounded: " << roundedPrice << std::endl;

    return 0;
}

Operators

cpp
#include <iostream>
#include <cmath>
#include <bitset>

int main() {

    // ── ARITHMETIC OPERATORS ─────────────────────────────────
    int a = 17, b = 5;
    std::cout << "a + b = " << (a + b) << std::endl;  // 22
    std::cout << "a - b = " << (a - b) << std::endl;  // 12
    std::cout << "a * b = " << (a * b) << std::endl;  // 85
    std::cout << "a / b = " << (a / b) << std::endl;  // 3 (integer division!)
    std::cout << "a % b = " << (a % b) << std::endl;  // 2 (modulus)

    double da = 17.0, db = 5.0;
    std::cout << "17.0 / 5.0 = " << (da / db) << std::endl;  // 3.4

    // Increment / Decrement
    int x = 10;
    std::cout << x++ << std::endl;  // 10 (post-increment: use then increment)
    std::cout << x   << std::endl;  // 11
    std::cout << ++x << std::endl;  // 12 (pre-increment: increment then use)

    // ── COMPARISON OPERATORS ─────────────────────────────────
    std::cout << std::boolalpha;    // Print bool as true/false
    std::cout << (5 == 5)  << std::endl;  // true
    std::cout << (5 != 3)  << std::endl;  // true
    std::cout << (5 > 3)   << std::endl;  // true
    std::cout << (5 < 3)   << std::endl;  // false
    std::cout << (5 >= 5)  << std::endl;  // true

    // ── LOGICAL OPERATORS ────────────────────────────────────
    bool p = true, q = false;
    std::cout << (p && q) << std::endl;  // false (AND)
    std::cout << (p || q) << std::endl;  // true  (OR)
    std::cout << (!p)     << std::endl;  // false (NOT)

    // Short-circuit evaluation
    int val = 0;
    bool result = (val != 0) && (10 / val > 1); // Safe: right side not evaluated
    std::cout << result << std::endl;  // false

    // ── ASSIGNMENT OPERATORS ─────────────────────────────────
    int n = 10;
    n += 5;   // n = n + 5  = 15
    n -= 3;   // n = n - 3  = 12
    n *= 2;   // n = n * 2  = 24
    n /= 4;   // n = n / 4  = 6
    n %= 4;   // n = n % 4  = 2
    n <<= 2;  // n = n << 2 = 8  (left shift — multiply by 2^2)
    n >>= 1;  // n = n >> 1 = 4  (right shift — divide by 2^1)
    std::cout << "n = " << n << std::endl;

    // ── BITWISE OPERATORS ────────────────────────────────────
    int bits1 = 0b1010;  // 10 in decimal
    int bits2 = 0b1100;  // 12 in decimal
    std::cout << std::bitset<4>(bits1 & bits2) << std::endl; // AND: 1000
    std::cout << std::bitset<4>(bits1 | bits2) << std::endl; // OR:  1110
    std::cout << std::bitset<4>(bits1 ^ bits2) << std::endl; // XOR: 0110
    std::cout << std::bitset<4>(~bits1)         << std::endl; // NOT

    // ── TERNARY OPERATOR ─────────────────────────────────────
    int score = 75;
    std::string grade = (score >= 60) ? "Pass" : "Fail";
    std::cout << "Result: " << grade << std::endl;

    // ── sizeof OPERATOR ──────────────────────────────────────
    std::cout << "sizeof(int) = " << sizeof(int) << " bytes" << std::endl;

    return 0;
}

Chapter 2: Control Flow

cpp
#include <iostream>
#include <string>

int main() {

    // ── IF / ELSE IF / ELSE ──────────────────────────────────
    int score = 87;
    std::string grade;

    if (score >= 90) {
        grade = "A";
    } else if (score >= 80) {
        grade = "B";
    } else if (score >= 70) {
        grade = "C";
    } else if (score >= 60) {
        grade = "D";
    } else {
        grade = "F";
    }

    std::cout << "Grade: " << grade << std::endl;

    // ── SWITCH STATEMENT ─────────────────────────────────────
    int day = 3;
    switch (day) {
        case 1: std::cout << "Monday" << std::endl; break;
        case 2: std::cout << "Tuesday" << std::endl; break;
        case 3: std::cout << "Wednesday" << std::endl; break;
        case 4: std::cout << "Thursday" << std::endl; break;
        case 5: std::cout << "Friday" << std::endl; break;
        case 6:
        case 7: std::cout << "Weekend!" << std::endl; break;
        default: std::cout << "Invalid day" << std::endl;
    }

    // ── FOR LOOP ─────────────────────────────────────────────
    // Classic for loop
    for (int i = 1; i <= 5; i++) {
        std::cout << i << " ";
    }
    std::cout << std::endl;  // 1 2 3 4 5

    // Nested for loop — multiplication table
    for (int i = 1; i <= 5; i++) {
        for (int j = 1; j <= 5; j++) {
            std::cout << (i * j) << "\t";
        }
        std::cout << std::endl;
    }

    // ── RANGE-BASED FOR LOOP (C++11) ─────────────────────────
    std::vector<std::string> courses = {
        "Python", "C++", "JavaScript", "DevOps", "Machine Learning"
    };

    for (const auto& course : courses) {
        std::cout << "Course: " << course << std::endl;
    }

    // ── WHILE LOOP ───────────────────────────────────────────
    int count = 1;
    while (count <= 5) {
        std::cout << count << " ";
        count++;
    }
    std::cout << std::endl;

    // ── DO-WHILE LOOP (runs at least once) ───────────────────
    int userInput;
    do {
        std::cout << "Enter a number between 1 and 10: ";
        std::cin >> userInput;
    } while (userInput < 1 || userInput > 10);

    std::cout << "You entered: " << userInput << std::endl;

    // ── LOOP CONTROL ─────────────────────────────────────────
    for (int i = 0; i < 10; i++) {
        if (i == 3) continue;   // Skip 3
        if (i == 7) break;      // Stop at 7
        std::cout << i << " ";  // 0 1 2 4 5 6
    }

    // ── GOTO (avoid — only for emergency exits in nested loops) ──
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (i == 1 && j == 1) goto loop_exit;
            std::cout << "(" << i << "," << j << ") ";
        }
    }
    loop_exit:
    std::cout << std::endl;

    return 0;
}

Chapter 3: Functions

cpp
#include <iostream>
#include <string>
#include <vector>
#include <functional>  // For std::function

// ── FUNCTION DECLARATION (prototype) ────────────────────────
double calculateBMI(double weight, double height);
void printSeparator(char ch = '-', int width = 40); // Default parameters

int main() {

    // Call functions defined after main
    std::cout << "BMI: " << calculateBMI(70.0, 1.75) << std::endl;
    printSeparator();
    printSeparator('=', 50);

    // ── INLINE FUNCTIONS ────────────────────────────────────
    // Defined with lambda or inline keyword
    auto square = [](int n) { return n * n; };
    std::cout << "Square of 7: " << square(7) << std::endl;

    // ── OVERLOADED FUNCTIONS ─────────────────────────────────
    // Same function name, different parameter types
    auto print = [](auto value) {
        std::cout << "Value: " << value << std::endl;
    };
    print(42);
    print(3.14);
    print("Hello");

    return 0;
}

// ── FUNCTION DEFINITIONS ────────────────────────────────────

double calculateBMI(double weight, double height) {
    if (height <= 0) throw std::invalid_argument("Height must be positive");
    double bmi = weight / (height * height);
    return bmi;
}

void printSeparator(char ch, int width) {
    for (int i = 0; i < width; i++) std::cout << ch;
    std::cout << std::endl;
}
cpp
// functions_advanced.cpp — Advanced Function Concepts

#include <iostream>
#include <vector>
#include <functional>
#include <algorithm>
#include <numeric>

// ── PASS BY VALUE vs REFERENCE vs POINTER ───────────────────

// Pass by value — function gets a COPY (original unchanged)
void passByValue(int x) {
    x = 100;  // Modifies local copy only
    std::cout << "Inside passByValue: " << x << std::endl;
}

// Pass by reference — function gets the ORIGINAL (can modify)
void passByReference(int& x) {
    x = 100;  // Modifies the original variable
    std::cout << "Inside passByRef: " << x << std::endl;
}

// Pass by const reference — efficient for large objects, read-only
void printCourse(const std::string& name) {
    std::cout << "Course: " << name << std::endl;
    // name = "changed";  // ❌ Compile error — const reference!
}

// Pass by pointer
void passByPointer(int* x) {
    if (x != nullptr) {
        *x = 100;  // Dereference and modify
    }
}

// ── RETURN BY REFERENCE ──────────────────────────────────────
int& getLarger(int& a, int& b) {
    return (a > b) ? a : b;  // Returns reference to larger
}

// ── VARIADIC FUNCTIONS (variable number of arguments) ───────
template<typename... Args>
void printAll(Args... args) {
    // Fold expression (C++17)
    ((std::cout << args << " "), ...);
    std::cout << std::endl;
}

// ── LAMBDA FUNCTIONS (C++11) ─────────────────────────────────
void demonstrateLambdas() {
    // Basic lambda
    auto add = [](int a, int b) -> int { return a + b; };
    std::cout << "Sum: " << add(3, 4) << std::endl;

    // Lambda capturing by value [=]
    int multiplier = 3;
    auto multiplyBy = [multiplier](int x) { return x * multiplier; };
    std::cout << "5 × 3 = " << multiplyBy(5) << std::endl;

    // Lambda capturing by reference [&]
    int total = 0;
    auto addToTotal = [&total](int x) { total += x; };
    addToTotal(10);
    addToTotal(20);
    addToTotal(30);
    std::cout << "Total: " << total << std::endl;  // 60

    // Lambda in STL algorithms (very common pattern)
    std::vector<int> numbers = {5, 2, 8, 1, 9, 3, 7, 4, 6};

    // Sort descending
    std::sort(numbers.begin(), numbers.end(),
              [](int a, int b) { return a > b; });

    // Filter: find all numbers > 5
    std::vector<int> large;
    std::copy_if(numbers.begin(), numbers.end(),
                 std::back_inserter(large),
                 [](int n) { return n > 5; });

    // Transform: square each element
    std::vector<int> squared(numbers.size());
    std::transform(numbers.begin(), numbers.end(),
                   squared.begin(),
                   [](int n) { return n * n; });

    // Accumulate with custom operation
    int product = std::accumulate(numbers.begin(), numbers.end(), 1,
                                  [](int acc, int n) { return acc * n; });

    std::cout << "Product: " << product << std::endl;

    // Mutable lambda — can modify captured copies
    int x = 10;
    auto increment = [x]() mutable { x++; return x; };
    std::cout << increment() << std::endl;  // 11
    std::cout << "x unchanged: " << x << std::endl;  // 10
}

// ── RECURSIVE FUNCTIONS ───────────────────────────────────────
long long factorial(int n) {
    if (n < 0) throw std::invalid_argument("Negative factorial");
    if (n == 0 || n == 1) return 1;  // Base case
    return n * factorial(n - 1);      // Recursive case
}

// Tail-recursive version (more efficient)
long long factorialTail(int n, long long accumulator = 1) {
    if (n <= 1) return accumulator;
    return factorialTail(n - 1, n * accumulator);
}

// Fibonacci with memoization
#include <unordered_map>
long long fibonacci(int n, std::unordered_map<int, long long>& memo) {
    if (n <= 1) return n;
    if (memo.count(n)) return memo[n];  // Cache hit
    memo[n] = fibonacci(n-1, memo) + fibonacci(n-2, memo);
    return memo[n];
}

// ── FUNCTION POINTERS AND std::function ─────────────────────
int applyOperation(int a, int b, std::function<int(int, int)> op) {
    return op(a, b);
}

int main() {
    // Pass by value/reference demo
    int num = 50;
    passByValue(num);
    std::cout << "After passByValue: " << num << std::endl;  // 50 (unchanged)

    passByReference(num);
    std::cout << "After passByRef: " << num << std::endl;    // 100 (changed!)

    // Return by reference
    int a = 10, b = 20;
    getLarger(a, b) = 999;  // Assign to the returned reference!
    std::cout << "b = " << b << std::endl;  // b = 999

    // Variadic template
    printAll(1, "hello", 3.14, true);

    // Lambdas demo
    demonstrateLambdas();

    // Factorial
    for (int i = 0; i <= 10; i++) {
        std::cout << i << "! = " << factorial(i) << std::endl;
    }

    // Fibonacci with memoization
    std::unordered_map<int, long long> memo;
    std::cout << "Fibonacci(50) = " << fibonacci(50, memo) << std::endl;

    // Function pointer
    auto result = applyOperation(10, 3, [](int a, int b) { return a + b; });
    std::cout << "10 + 3 = " << result << std::endl;

    return 0;
}

Chapter 4: Pointers and References

Pointers are one of C++’s most powerful — and most misunderstood — features. Mastering them is essential for systems programming, data structures, and performance optimization.

cpp
#include <iostream>
#include <string>
#include <memory>   // For smart pointers

void demonstratePointers() {
    // ── POINTER BASICS ────────────────────────────────────────
    int value = 42;

    // Declare pointer — stores memory ADDRESS of an int
    int* ptr = &value;    // & is "address-of" operator

    std::cout << "Value:   " << value << std::endl;   // 42
    std::cout << "Address: " << &value << std::endl;  // e.g., 0x7fff1234
    std::cout << "ptr:     " << ptr << std::endl;     // Same address
    std::cout << "*ptr:    " << *ptr << std::endl;    // 42 (* is dereference)

    // Modify through pointer
    *ptr = 100;
    std::cout << "After *ptr = 100: " << value << std::endl;  // 100

    // ── POINTER ARITHMETIC ────────────────────────────────────
    int arr[5] = {10, 20, 30, 40, 50};
    int* arrPtr = arr;  // Points to first element

    std::cout << *arrPtr << std::endl;       // 10
    std::cout << *(arrPtr + 1) << std::endl; // 20 (next int in memory)
    std::cout << *(arrPtr + 4) << std::endl; // 50 (last element)

    // Iterate array with pointer
    for (int i = 0; i < 5; i++) {
        std::cout << *(arrPtr + i) << " ";
    }
    std::cout << std::endl;

    // ── NULL / NULLPTR (C++11) ────────────────────────────────
    int* nullPtr = nullptr;    // Modern C++ null pointer (use instead of NULL)
    if (nullPtr == nullptr) {
        std::cout << "Pointer is null" << std::endl;
    }
    // NEVER dereference a null pointer — undefined behavior / crash!

    // ── CONST POINTERS ────────────────────────────────────────
    int x = 10, y = 20;

    const int* ptr1 = &x;  // Pointer to const int
    // *ptr1 = 20;          // ❌ Cannot modify value through ptr1
    ptr1 = &y;             // ✅ Can point to different int

    int* const ptr2 = &x;  // Const pointer to int
    *ptr2 = 30;            // ✅ Can modify value
    // ptr2 = &y;           // ❌ Cannot point to different variable

    const int* const ptr3 = &x;  // Const pointer to const int
    // Neither pointer nor value can change

    // ── REFERENCES ────────────────────────────────────────────
    int original = 50;
    int& ref = original;    // Reference — another name for original

    ref = 100;              // Modifies original through reference
    std::cout << "original = " << original << std::endl;  // 100

    // References vs Pointers:
    // - References cannot be null
    // - References cannot be reassigned to refer to different variable
    // - References don't need dereferencing operator
    // - References are safer and cleaner for function parameters

    // ── POINTER TO POINTER (Double Pointer) ───────────────────
    int** pptr = &ptr;  // Pointer to pointer to int
    std::cout << "**pptr = " << **pptr << std::endl;  // Value at ptr's address

    // ── VOID POINTER (Generic Pointer) ────────────────────────
    void* vPtr = &value;
    // *vPtr = 10;  // ❌ Cannot dereference void pointer directly
    int* typedPtr = static_cast<int*>(vPtr);
    std::cout << "*typedPtr = " << *typedPtr << std::endl;
}

// ── DYNAMIC MEMORY ALLOCATION ─────────────────────────────────
void demonstrateDynamicMemory() {
    // Allocate single value on heap
    int* dynamicInt = new int(42);
    std::cout << "Dynamic int: " << *dynamicInt << std::endl;
    delete dynamicInt;      // Must free heap memory — memory leak if forgotten!
    dynamicInt = nullptr;   // Good practice: set to null after delete

    // Allocate array on heap
    int size = 5;
    int* dynamicArr = new int[size]{1, 2, 3, 4, 5};
    for (int i = 0; i < size; i++) {
        std::cout << dynamicArr[i] << " ";
    }
    delete[] dynamicArr;    // Use delete[] for arrays
    dynamicArr = nullptr;

    // ⚠️ Memory Management Rules:
    // 1. Every new must have a matching delete
    // 2. Every new[] must have a matching delete[]
    // 3. Never delete the same pointer twice
    // 4. Never use after delete (dangling pointer)
}

// ── SMART POINTERS (C++11) — PREFER THESE OVER RAW POINTERS ──
void demonstrateSmartPointers() {

    // unique_ptr — sole ownership, auto-deleted when out of scope
    {
        std::unique_ptr<int> uPtr = std::make_unique<int>(42);
        std::cout << "unique_ptr value: " << *uPtr << std::endl;
        // No delete needed! Automatically freed at end of scope

        // Transfer ownership with std::move
        std::unique_ptr<int> uPtr2 = std::move(uPtr);
        // uPtr is now nullptr — ownership transferred
        std::cout << "After move: " << *uPtr2 << std::endl;
    } // uPtr2 automatically deleted here

    // shared_ptr — shared ownership, ref-counted
    {
        std::shared_ptr<std::string> sPtr1 =
            std::make_shared<std::string>("elearncourses.com");

        {
            std::shared_ptr<std::string> sPtr2 = sPtr1;  // Share ownership
            std::cout << "Count: " << sPtr1.use_count() << std::endl;  // 2
            std::cout << *sPtr2 << std::endl;
        } // sPtr2 destroyed, count decrements to 1

        std::cout << "Count: " << sPtr1.use_count() << std::endl;  // 1
    } // sPtr1 destroyed, count 0 — memory freed

    // weak_ptr — observe without owning (prevents circular references)
    std::shared_ptr<int> shared = std::make_shared<int>(100);
    std::weak_ptr<int> weak = shared;  // Doesn't increase ref count

    if (auto locked = weak.lock()) {  // Check if still alive
        std::cout << "Weak ptr value: " << *locked << std::endl;
    }
}

int main() {
    demonstratePointers();
    demonstrateDynamicMemory();
    demonstrateSmartPointers();
    return 0;
}

Chapter 5: Object-Oriented Programming in C++

cpp
// oop_complete.cpp — Complete OOP in C++

#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <memory>
#include <cmath>

// ── CLASS DEFINITION ──────────────────────────────────────────

class Student {
private:
    // Private member variables (data hiding — encapsulation)
    std::string m_id;
    std::string m_name;
    std::string m_email;
    std::string m_course;
    std::vector<double> m_scores;
    static int s_totalStudents;  // Static: shared across all instances

public:
    // ── CONSTRUCTORS ──────────────────────────────────────────

    // Default constructor
    Student() : m_id(""), m_name("Unknown"), m_email(""),
                m_course(""), m_scores() {
        s_totalStudents++;
    }

    // Parameterized constructor with initializer list
    Student(std::string id, std::string name,
            std::string email, std::string course)
        : m_id(std::move(id)),
          m_name(std::move(name)),
          m_email(std::move(email)),
          m_course(std::move(course)),
          m_scores() {
        s_totalStudents++;
    }

    // Copy constructor
    Student(const Student& other)
        : m_id(other.m_id + "_copy"),
          m_name(other.m_name),
          m_email(other.m_email),
          m_course(other.m_course),
          m_scores(other.m_scores) {
        s_totalStudents++;
        std::cout << "Copy constructor called for " << m_name << std::endl;
    }

    // Move constructor (C++11) — efficient transfer of resources
    Student(Student&& other) noexcept
        : m_id(std::move(other.m_id)),
          m_name(std::move(other.m_name)),
          m_email(std::move(other.m_email)),
          m_course(std::move(other.m_course)),
          m_scores(std::move(other.m_scores)) {
        s_totalStudents++;
        std::cout << "Move constructor called" << std::endl;
    }

    // ── DESTRUCTOR ────────────────────────────────────────────
    ~Student() {
        s_totalStudents--;
        // Release any dynamic resources here
    }

    // ── GETTERS (Accessors) ───────────────────────────────────
    const std::string& getId()     const { return m_id; }
    const std::string& getName()   const { return m_name; }
    const std::string& getEmail()  const { return m_email; }
    const std::string& getCourse() const { return m_course; }

    // ── SETTERS (Mutators) with validation ────────────────────
    void setName(const std::string& name) {
        if (name.empty()) throw std::invalid_argument("Name cannot be empty");
        m_name = name;
    }

    void setEmail(const std::string& email) {
        if (email.find('@') == std::string::npos)
            throw std::invalid_argument("Invalid email address");
        m_email = email;
    }

    // ── MEMBER METHODS ────────────────────────────────────────
    void addScore(double score) {
        if (score < 0 || score > 100)
            throw std::out_of_range("Score must be 0-100");
        m_scores.push_back(score);
    }

    double getAverage() const {
        if (m_scores.empty()) return 0.0;
        double sum = 0;
        for (double s : m_scores) sum += s;
        return sum / m_scores.size();
    }

    std::string getGrade() const {
        double avg = getAverage();
        if (avg >= 90) return "A";
        if (avg >= 80) return "B";
        if (avg >= 70) return "C";
        if (avg >= 60) return "D";
        return "F";
    }

    // ── STATIC METHOD ─────────────────────────────────────────
    static int getTotalStudents() { return s_totalStudents; }

    // ── OPERATOR OVERLOADING ──────────────────────────────────
    bool operator==(const Student& other) const {
        return m_id == other.m_id;
    }

    bool operator<(const Student& other) const {
        return getAverage() < other.getAverage();
    }

    // Assignment operator
    Student& operator=(const Student& other) {
        if (this != &other) {  // Self-assignment check
            m_id = other.m_id;
            m_name = other.m_name;
            m_email = other.m_email;
            m_course = other.m_course;
            m_scores = other.m_scores;
        }
        return *this;
    }

    // Stream output operator (friend function)
    friend std::ostream& operator<<(std::ostream& os, const Student& s) {
        os << "Student[" << s.m_id << "]: " << s.m_name
           << " | Course: " << s.m_course
           << " | Avg: " << s.getAverage()
           << " | Grade: " << s.getGrade();
        return os;
    }

    void display() const {
        std::cout << *this << std::endl;
    }
};

// Initialize static member
int Student::s_totalStudents = 0;


// ── INHERITANCE ───────────────────────────────────────────────

class PremiumStudent : public Student {
private:
    std::string m_subscriptionTier;
    int m_mentorSessions;
    double m_monthlyFee;

public:
    PremiumStudent(std::string id, std::string name,
                   std::string email, std::string course,
                   std::string tier, double fee)
        : Student(std::move(id), std::move(name),
                  std::move(email), std::move(course)),
          m_subscriptionTier(std::move(tier)),
          m_mentorSessions(0),
          m_monthlyFee(fee) {}

    void bookMentorSession(const std::string& mentorName) {
        m_mentorSessions++;
        std::cout << "📅 " << getName() << " booked session #"
                  << m_mentorSessions << " with " << mentorName << std::endl;
    }

    std::string getTier() const { return m_subscriptionTier; }
    double getMonthlyFee() const { return m_monthlyFee; }

    // Override base class method
    friend std::ostream& operator<<(std::ostream& os, const PremiumStudent& ps) {
        os << static_cast<const Student&>(ps)  // Base class part
           << " | Tier: " << ps.m_subscriptionTier
           << " | Sessions: " << ps.m_mentorSessions;
        return os;
    }
};


// ── ABSTRACT CLASS AND POLYMORPHISM ──────────────────────────

// Abstract base class — defines interface
class Shape {
protected:
    std::string m_color;
    std::string m_name;

public:
    Shape(std::string name, std::string color = "white")
        : m_name(std::move(name)), m_color(std::move(color)) {}

    // Pure virtual functions — must be implemented by derived classes
    virtual double area() const = 0;
    virtual double perimeter() const = 0;
    virtual void draw() const = 0;

    // Virtual function with default implementation
    virtual std::string describe() const {
        return m_name + " [" + m_color + "] - Area: " +
               std::to_string(area());
    }

    // Virtual destructor — ESSENTIAL for polymorphism!
    virtual ~Shape() = default;

    // Non-virtual — same for all shapes
    std::string getName()  const { return m_name; }
    std::string getColor() const { return m_color; }
};

class Circle : public Shape {
private:
    double m_radius;
    static constexpr double PI = 3.14159265358979;

public:
    explicit Circle(double radius, std::string color = "red")
        : Shape("Circle", std::move(color)), m_radius(radius) {
        if (radius <= 0) throw std::invalid_argument("Radius must be positive");
    }

    double area()      const override { return PI * m_radius * m_radius; }
    double perimeter() const override { return 2 * PI * m_radius; }

    void draw() const override {
        std::cout << "Drawing " << m_color << " circle with radius "
                  << m_radius << std::endl;
    }

    double getRadius() const { return m_radius; }
};

class Rectangle : public Shape {
private:
    double m_width, m_height;

public:
    Rectangle(double width, double height, std::string color = "blue")
        : Shape("Rectangle", std::move(color)),
          m_width(width), m_height(height) {
        if (width <= 0 || height <= 0)
            throw std::invalid_argument("Dimensions must be positive");
    }

    double area()      const override { return m_width * m_height; }
    double perimeter() const override { return 2 * (m_width + m_height); }

    void draw() const override {
        std::cout << "Drawing " << m_color << " rectangle "
                  << m_width << "×" << m_height << std::endl;
    }
};

class Triangle : public Shape {
private:
    double m_a, m_b, m_c;  // Three sides

public:
    Triangle(double a, double b, double c, std::string color = "green")
        : Shape("Triangle", std::move(color)), m_a(a), m_b(b), m_c(c) {
        if (a + b <= c || a + c <= b || b + c <= a)
            throw std::invalid_argument("Invalid triangle sides");
    }

    double perimeter() const override { return m_a + m_b + m_c; }

    double area() const override {
        double s = perimeter() / 2;  // Semi-perimeter
        return std::sqrt(s * (s-m_a) * (s-m_b) * (s-m_c));  // Heron's formula
    }

    void draw() const override {
        std::cout << "Drawing " << m_color << " triangle with sides "
                  << m_a << ", " << m_b << ", " << m_c << std::endl;
    }
};

// ── POLYMORPHISM IN ACTION ────────────────────────────────────
void processShapes(const std::vector<std::unique_ptr<Shape>>& shapes) {
    double totalArea = 0;

    std::cout << "\n=== Shape Analysis ===" << std::endl;
    for (const auto& shape : shapes) {
        shape->draw();
        std::cout << "  Area: " << shape->area()
                  << " | Perimeter: " << shape->perimeter() << std::endl;
        totalArea += shape->area();
    }
    std::cout << "Total area: " << totalArea << std::endl;
}

int main() {
    // ── Student system ────────────────────────────────────────
    std::cout << "=== Student Management System ===" << std::endl;

    Student alice("S001", "Alice Johnson",
                  "alice@example.com", "C++ Tutorial");
    alice.addScore(92.5);
    alice.addScore(88.0);
    alice.addScore(95.5);
    alice.addScore(79.0);

    std::cout << alice << std::endl;

    PremiumStudent bob("S002", "Bob Smith",
                       "bob@example.com", "C++ Tutorial",
                       "Platinum", 4999.0);
    bob.addScore(78.0);
    bob.addScore(85.5);
    bob.bookMentorSession("Dr. Stroustrup");
    bob.bookMentorSession("Prof. Sutter");

    std::cout << "Total students: " << Student::getTotalStudents() << std::endl;

    // ── Polymorphism with shapes ──────────────────────────────
    std::cout << "\n=== Polymorphism Demo ===" << std::endl;

    std::vector<std::unique_ptr<Shape>> shapes;
    shapes.push_back(std::make_unique<Circle>(5.0, "red"));
    shapes.push_back(std::make_unique<Rectangle>(8.0, 4.0, "blue"));
    shapes.push_back(std::make_unique<Triangle>(3.0, 4.0, 5.0, "green"));
    shapes.push_back(std::make_unique<Circle>(2.5, "yellow"));

    processShapes(shapes);

    // Runtime polymorphism — correct method called based on actual type
    for (const auto& shape : shapes) {
        std::cout << shape->describe() << std::endl;
    }

    return 0;
}

Chapter 6: Templates — Generic Programmingcpp

#include <iostream>
#include <vector>
#include <stdexcept>
#include <string>
#include <algorithm>

// ── FUNCTION TEMPLATES ────────────────────────────────────────

template<typename T>
T getMax(T a, T b) {
    return (a > b) ? a : b;
}

template<typename T>
T getMin(T a, T b) {
    return (a < b) ? a : b;
}

// Template with multiple type parameters
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

// Template specialization for string
template<>
std::string getMax<std::string>(std::string a, std::string b) {
    return (a.length() >= b.length()) ? a : b;  // Custom: longer string wins
}

// Variadic template
template<typename T, typename... Args>
T sum(T first, Args... rest) {
    if constexpr (sizeof...(rest) == 0)  // C++17 if constexpr
        return first;
    else
        return first + sum(rest...);
}

// ── CLASS TEMPLATES ───────────────────────────────────────────

template<typename T, int MaxSize = 100>
class Stack {
private:
    T m_data[MaxSize];
    int m_top;

public:
    Stack() : m_top(-1) {}

    void push(const T& value) {
        if (m_top >= MaxSize - 1)
            throw std::overflow_error("Stack overflow");
        m_data[++m_top] = value;
    }

    T pop() {
        if (isEmpty())
            throw std::underflow_error("Stack underflow");
        return m_data[m_top--];
    }

    T& peek() {
        if (isEmpty())
            throw std::underflow_error("Stack is empty");
        return m_data[m_top];
    }

    bool isEmpty() const { return m_top == -1; }
    int  size()    const { return m_top + 1; }
    int  capacity() const { return MaxSize; }

    void display() const {
        std::cout << "Stack [top→bottom]: ";
        for (int i = m_top; i >= 0; i--)
            std::cout << m_data[i] << " ";
        std::cout << std::endl;
    }
};

// ── TEMPLATE WITH CONSTRAINTS (C++20 Concepts) ────────────────

// Before C++20 — use SFINAE or static_assert
template<typename T>
T absolute(T value) {
    static_assert(std::is_arithmetic_v<T>,
                  "absolute() requires numeric type");
    return value < 0 ? -value : value;
}

// C++20 Concept syntax
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;

template<Numeric T>
T square(T value) { return value * value; }

int main() {
    // Function templates
    std::cout << "Max(3, 7) = "        << getMax(3, 7)         << std::endl;
    std::cout << "Max(3.14, 2.71) = " << getMax(3.14, 2.71)   << std::endl;
    std::cout << "Max strings: "       << getMax<std::string>(
        "Hello", "Hi there"
    ) << std::endl;

    // Mixed types with auto return
    std::cout << "add(3, 2.5) = "     << add(3, 2.5)          << std::endl;

    // Variadic sum
    std::cout << "sum(1,2,3,4,5) = " << sum(1, 2, 3, 4, 5) << std::endl;

    // Stack template
    Stack<int, 10> intStack;
    Stack<std::string, 5> strStack;

    intStack.push(10);
    intStack.push(20);
    intStack.push(30);
    intStack.display();
    std::cout << "Popped: " << intStack.pop() << std::endl;

    strStack.push("Python");
    strStack.push("C++");
    strStack.push("Java");
    strStack.display();

    // Template constraints
    std::cout << "absolute(-42) = "   << absolute(-42)         << std::endl;
    std::cout << "absolute(-3.14) = " << absolute(-3.14)       << std::endl;
    std::cout << "square(7) = "       << square(7)             << std::endl;

    return 0;
}

Chapter 7: The C++ Standard Template Library (STL)

cpp
#include <iostream>
#include <vector>
#include <list>
#include <deque>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <stack>
#include <queue>
#include <priority_queue>
#include <algorithm>
#include <numeric>
#include <iterator>
#include <string>
#include <sstream>

void demonstrateContainers() {

    std::cout << "=== STL CONTAINERS ===" << std::endl;

    // ── VECTOR — Dynamic array (most used container) ─────────
    std::vector<int> vec = {5, 2, 8, 1, 9, 3};
    vec.push_back(7);            // O(1) amortized
    vec.insert(vec.begin(), 0);  // O(n) — insert at front
    vec.erase(vec.begin() + 2);  // O(n) — erase by position
    vec.reserve(20);             // Pre-allocate capacity

    std::cout << "Vector size: " << vec.size()
              << " capacity: "   << vec.capacity() << std::endl;

    // ── MAP — Sorted key-value pairs ─────────────────────────
    std::map<std::string, int> courseRatings;
    courseRatings["Python Tutorial"]     = 98;
    courseRatings["C++ Tutorial"]        = 96;
    courseRatings["JavaScript Tutorial"] = 94;
    courseRatings["DevOps Tutorial"]     = 92;

    // Maps are sorted by key automatically
    for (const auto& [course, rating] : courseRatings) {
        std::cout << course << ": " << rating << std::endl;
    }

    // Safe access with find
    auto it = courseRatings.find("Python Tutorial");
    if (it != courseRatings.end()) {
        std::cout << "Found: " << it->second << std::endl;
    }

    // ── UNORDERED_MAP — Hash map O(1) average access ─────────
    std::unordered_map<std::string, int> studentScores;
    studentScores["Alice"]   = 95;
    studentScores["Bob"]     = 87;
    studentScores["Charlie"] = 92;

    std::cout << "Alice's score: " << studentScores.at("Alice") << std::endl;
    std::cout << "Contains Bob: " << studentScores.count("Bob") << std::endl;

    // ── SET — Unique sorted elements ─────────────────────────
    std::set<int> uniqueNums = {5, 2, 8, 2, 1, 5, 9};
    // Duplicates automatically removed: {1, 2, 5, 8, 9}
    for (int n : uniqueNums) std::cout << n << " ";
    std::cout << std::endl;

    // ── PRIORITY_QUEUE — Max-heap by default ─────────────────
    std::priority_queue<int> maxHeap;
    for (int x : {5, 2, 8, 1, 9, 3, 7}) maxHeap.push(x);

    std::cout << "Priority queue (max first): ";
    while (!maxHeap.empty()) {
        std::cout << maxHeap.top() << " ";  // 9 8 7 5 3 2 1
        maxHeap.pop();
    }
    std::cout << std::endl;

    // Min-heap
    std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;
    for (int x : {5, 2, 8, 1, 9, 3}) minHeap.push(x);
    std::cout << "Min heap top: " << minHeap.top() << std::endl; // 1
}

void demonstrateAlgorithms() {

    std::cout << "\n=== STL ALGORITHMS ===" << std::endl;

    std::vector<int> nums = {5, 2, 8, 1, 9, 3, 7, 4, 6};

    // ── SORTING ───────────────────────────────────────────────
    std::sort(nums.begin(), nums.end());                    // Ascending
    std::sort(nums.begin(), nums.end(), std::greater<int>()); // Descending

    // Partial sort — only sort first 4 elements
    std::partial_sort(nums.begin(), nums.begin() + 4, nums.end());

    // Stable sort — preserves relative order of equal elements
    std::vector<std::pair<int, std::string>> data = {
        {3, "C"}, {1, "A"}, {2, "B"}, {1, "D"}
    };
    std::stable_sort(data.begin(), data.end(),
                     [](const auto& a, const auto& b) {
                         return a.first < b.first;
                     });
    // {1,"A"}, {1,"D"}, {2,"B"}, {3,"C"} — "A" before "D" preserved

    // ── SEARCHING ─────────────────────────────────────────────
    std::sort(nums.begin(), nums.end());  // Must be sorted for binary search
    bool found = std::binary_search(nums.begin(), nums.end(), 7);
    std::cout << "7 found: " << std::boolalpha << found << std::endl;

    auto lb = std::lower_bound(nums.begin(), nums.end(), 5);
    auto ub = std::upper_bound(nums.begin(), nums.end(), 5);
    std::cout << "Position of 5: " << (lb - nums.begin()) << std::endl;

    auto minIt = std::min_element(nums.begin(), nums.end());
    auto maxIt = std::max_element(nums.begin(), nums.end());
    std::cout << "Min: " << *minIt << " Max: " << *maxIt << std::endl;

    // ── TRANSFORMATIONS ───────────────────────────────────────
    std::vector<int> result(nums.size());
    std::transform(nums.begin(), nums.end(), result.begin(),
                   [](int n) { return n * n; });

    // ── NUMERIC ALGORITHMS ────────────────────────────────────
    int total = std::accumulate(nums.begin(), nums.end(), 0);
    int product = std::accumulate(nums.begin(), nums.end(), 1,
                                   std::multiplies<int>());
    std::cout << "Sum: " << total << " Product: " << product << std::endl;

    // ── CONDITIONAL ALGORITHMS ────────────────────────────────
    bool allPositive = std::all_of(nums.begin(), nums.end(),
                                    [](int n) { return n > 0; });
    bool anyBig = std::any_of(nums.begin(), nums.end(),
                               [](int n) { return n > 8; });
    int countEven = std::count_if(nums.begin(), nums.end(),
                                   [](int n) { return n % 2 == 0; });

    std::cout << "All positive: " << allPositive << std::endl;
    std::cout << "Any > 8: " << anyBig << std::endl;
    std::cout << "Even count: " << countEven << std::endl;

    // ── COPY, MOVE, FILL ─────────────────────────────────────
    std::vector<int> dest(5);
    std::fill(dest.begin(), dest.end(), 42);    // Fill with value
    std::iota(dest.begin(), dest.end(), 1);     // Fill with 1,2,3,4,5

    // Unique — remove consecutive duplicates
    std::vector<int> withDups = {1, 1, 2, 3, 3, 3, 4, 5, 5};
    auto newEnd = std::unique(withDups.begin(), withDups.end());
    withDups.erase(newEnd, withDups.end());  // {1, 2, 3, 4, 5}

    // ── REVERSE AND ROTATE ────────────────────────────────────
    std::reverse(nums.begin(), nums.end());
    std::rotate(nums.begin(), nums.begin() + 3, nums.end());
}

int main() {
    demonstrateContainers();
    demonstrateAlgorithms();
    return 0;
}

Chapter 8: Exception Handling and File I/O

cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

// ── CUSTOM EXCEPTION CLASSES ──────────────────────────────────

class CourseException : public std::exception {
private:
    std::string m_message;
    std::string m_code;

public:
    CourseException(std::string msg, std::string code = "UNKNOWN")
        : m_message(std::move(msg)), m_code(std::move(code)) {}

    const char* what() const noexcept override {
        return m_message.c_str();
    }
    const std::string& getCode() const { return m_code; }
};

class EnrollmentException : public CourseException {
public:
    EnrollmentException(const std::string& msg)
        : CourseException(msg, "ENROLLMENT_ERR") {}
};

// ── EXCEPTION HANDLING ────────────────────────────────────────

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

int parseAge(const std::string& input) {
    try {
        int age = std::stoi(input);  // stoi throws if conversion fails
        if (age < 0 || age > 150)
            throw std::out_of_range("Age must be 0-150");
        return age;
    } catch (const std::invalid_argument& e) {
        throw std::invalid_argument("Invalid age format: " + input);
    }
}

void demonstrateExceptions() {
    // Multiple catch blocks
    try {
        double result = safeDivide(10.0, 0.0);
        std::cout << result << std::endl;
    } catch (const std::runtime_error& e) {
        std::cout << "Runtime Error: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cout << "Standard Exception: " << e.what() << std::endl;
    } catch (...) {
        std::cout << "Unknown exception caught" << std::endl;
    }

    // Custom exceptions
    try {
        throw EnrollmentException("Course is full — max 50 students");
    } catch (const EnrollmentException& e) {
        std::cout << "[" << e.getCode() << "] " << e.what() << std::endl;
    } catch (const CourseException& e) {
        std::cout << "Course error: " << e.what() << std::endl;
    }

    // Exception in constructor
    std::vector<std::string> inputs = {"25", "abc", "200", "30"};
    for (const auto& input : inputs) {
        try {
            int age = parseAge(input);
            std::cout << "Valid age: " << age << std::endl;
        } catch (const std::exception& e) {
            std::cout << "Error: " << e.what() << std::endl;
        }
    }
}

// ── FILE I/O ──────────────────────────────────────────────────

void writeStudentData() {
    std::ofstream outFile("students.csv");
    if (!outFile.is_open())
        throw std::runtime_error("Cannot open students.csv for writing");

    // Write header
    outFile << "ID,Name,Course,Score1,Score2,Score3\n";

    // Write data
    struct StudentRecord {
        std::string id, name, course;
        std::vector<double> scores;
    };

    std::vector<StudentRecord> students = {
        {"S001", "Alice Johnson", "C++ Tutorial", {92.5, 88.0, 95.5}},
        {"S002", "Bob Smith",     "Python",       {78.0, 85.5, 82.0}},
        {"S003", "Charlie Brown", "JavaScript",   {91.0, 94.5, 88.5}}
    };

    for (const auto& s : students) {
        outFile << s.id << "," << s.name << "," << s.course;
        for (double score : s.scores) {
            outFile << "," << score;
        }
        outFile << "\n";
    }

    outFile.close();
    std::cout << "✅ Student data written to students.csv" << std::endl;
}

void readStudentData() {
    std::ifstream inFile("students.csv");
    if (!inFile.is_open())
        throw std::runtime_error("Cannot open students.csv for reading");

    std::string line;
    int lineNum = 0;

    while (std::getline(inFile, line)) {
        lineNum++;
        if (lineNum == 1) {
            std::cout << "Header: " << line << std::endl;
            continue;
        }

        std::istringstream ss(line);
        std::string token;
        std::vector<std::string> fields;

        while (std::getline(ss, token, ',')) {
            fields.push_back(token);
        }

        if (fields.size() >= 6) {
            std::cout << "Student: " << fields[1]
                      << " | Course: " << fields[2]
                      << " | Scores: " << fields[3]
                      << " " << fields[4] << " " << fields[5]
                      << std::endl;
        }
    }
    inFile.close();
}

void appendToLog(const std::string& message) {
    std::ofstream logFile("app.log", std::ios::app);  // Append mode
    if (!logFile) throw std::runtime_error("Cannot open log file");

    // Get current time
    time_t now = time(nullptr);
    char timeStr[26];
    ctime_r(&now, timeStr);
    timeStr[24] = '\0';  // Remove newline

    logFile << "[" << timeStr << "] " << message << "\n";
}

int main() {
    demonstrateExceptions();

    try {
        writeStudentData();
        readStudentData();
        appendToLog("Application started successfully");
        appendToLog("Student data loaded");
    } catch (const std::exception& e) {
        std::cerr << "File I/O Error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

Chapter 9: Modern C++ Features (C++11/14/17/20)

cpp
#include <iostream>
#include <optional>     // C++17
#include <variant>      // C++17
#include <any>          // C++17
#include <string_view>  // C++17
#include <filesystem>   // C++17
#include <ranges>       // C++20
#include <format>       // C++20
#include <span>         // C++20
#include <chrono>
#include <thread>
#include <mutex>
#include <future>

// ── std::optional (C++17) ─────────────────────────────────────
// Represents a value that may or may not be present

std::optional<int> findStudentScore(
    const std::string& name,
    const std::map<std::string, int>& db
) {
    auto it = db.find(name);
    if (it == db.end()) return std::nullopt;  // No value
    return it->second;
}

void demonstrateOptional() {
    std::map<std::string, int> scores = {
        {"Alice", 95}, {"Bob", 87}, {"Charlie", 92}
    };

    auto aliceScore = findStudentScore("Alice", scores);
    auto eveScore   = findStudentScore("Eve", scores);

    if (aliceScore.has_value()) {
        std::cout << "Alice: " << *aliceScore << std::endl;
    }

    // value_or: default if empty
    std::cout << "Eve: " << eveScore.value_or(0) << std::endl;
}

// ── std::variant (C++17) — Type-safe union ───────────────────
using CourseResult = std::variant<int, std::string, double>;

CourseResult getCourseResult(int type) {
    switch (type) {
        case 0: return 95;            // int score
        case 1: return "Excellent!";  // string feedback
        case 2: return 4.8;           // double rating
        default: return std::string("Unknown");
    }
}

// ── std::string_view (C++17) — Non-owning string reference ───
void processCourseName(std::string_view name) {
    // No string copy — just a view into existing string data
    std::cout << "Processing: " << name
              << " (length: " << name.length() << ")" << std::endl;
}

// ── Structured Bindings (C++17) ───────────────────────────────
void demonstrateStructuredBindings() {
    // Pair
    std::pair<std::string, int> course = {"C++ Tutorial", 96};
    auto [title, rating] = course;  // Decompose into variables
    std::cout << title << ": " << rating << std::endl;

    // Tuple
    std::tuple<std::string, int, double> student = {"Alice", 25, 4.8};
    auto [name, age, gpa] = student;
    std::cout << name << " | Age: " << age << " | GPA: " << gpa << std::endl;

    // Map iteration
    std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}};
    for (const auto& [name, score] : scores) {
        std::cout << name << ": " << score << std::endl;
    }
}

// ── C++20 Ranges ──────────────────────────────────────────────
void demonstrateRanges() {
    std::vector<int> nums = {5, 2, 8, 1, 9, 3, 7, 4, 6};

    // Filter and transform with ranges pipeline
    auto result = nums
        | std::views::filter([](int n) { return n % 2 == 0; })  // evens
        | std::views::transform([](int n) { return n * n; })     // square
        | std::views::take(3);                                    // first 3

    std::cout << "C++20 Ranges result: ";
    for (int n : result) std::cout << n << " ";  // 4 64 16
    std::cout << std::endl;

    // Sort range (returns sorted view, doesn't modify)
    auto sorted = nums | std::views::all;
    std::ranges::sort(nums);
    std::cout << "Sorted: ";
    std::ranges::for_each(nums, [](int n) { std::cout << n << " "; });
    std::cout << std::endl;
}

// ── Multithreading (C++11) ────────────────────────────────────
std::mutex cout_mutex;

void workerTask(int id, int workload) {
    // Simulate work
    std::this_thread::sleep_for(std::chrono::milliseconds(100 * id));

    std::lock_guard<std::mutex> lock(cout_mutex);  // Thread-safe output
    std::cout << "Worker " << id << " completed " << workload
              << " tasks" << std::endl;
}

void demonstrateThreads() {
    std::cout << "\n=== Multithreading Demo ===" << std::endl;

    // Launch threads
    std::vector<std::thread> threads;
    for (int i = 1; i <= 4; i++) {
        threads.emplace_back(workerTask, i, i * 10);
    }

    // Wait for all threads to complete
    for (auto& t : threads) t.join();

    // std::async — async task with future result
    auto future = std::async(std::launch::async, []() {
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
        return 42;  // Return value
    });

    std::cout << "Doing other work while async runs..." << std::endl;
    int result = future.get();  // Wait and get result
    std::cout << "Async result: " << result << std::endl;
}

int main() {
    demonstrateOptional();
    demonstrateStructuredBindings();
    demonstrateRanges();
    demonstrateThreads();
    return 0;
}

Real-World C++ Project: Student Grade Management System

cpp
// grade_system.cpp — Complete Student Grade Management System

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <algorithm>
#include <numeric>
#include <iomanip>
#include <memory>
#include <stdexcept>
#include <string>
#include <optional>

struct CourseRecord {
    std::string subject;
    double score;
    int credits;
    std::string semester;
};

class StudentProfile {
private:
    std::string m_id;
    std::string m_name;
    std::string m_program;
    std::vector<CourseRecord> m_records;

public:
    StudentProfile(std::string id, std::string name, std::string program)
        : m_id(std::move(id)),
          m_name(std::move(name)),
          m_program(std::move(program)) {}

    void addCourse(const std::string& subject, double score,
                   int credits, const std::string& semester) {
        if (score < 0 || score > 100)
            throw std::invalid_argument("Score must be between 0 and 100");
        m_records.push_back({subject, score, credits, semester});
    }

    double calculateGPA() const {
        if (m_records.empty()) return 0.0;

        double totalPoints = 0.0;
        int totalCredits = 0;

        for (const auto& rec : m_records) {
            double gradePoint;
            if      (rec.score >= 90) gradePoint = 4.0;
            else if (rec.score >= 80) gradePoint = 3.0;
            else if (rec.score >= 70) gradePoint = 2.0;
            else if (rec.score >= 60) gradePoint = 1.0;
            else                      gradePoint = 0.0;

            totalPoints  += gradePoint * rec.credits;
            totalCredits += rec.credits;
        }

        return totalCredits > 0 ? totalPoints / totalCredits : 0.0;
    }

    std::string getLetterGrade(double score) const {
        if (score >= 90) return "A";
        if (score >= 80) return "B";
        if (score >= 70) return "C";
        if (score >= 60) return "D";
        return "F";
    }

    void generateTranscript() const {
        std::cout << "\n" << std::string(60, '=') << std::endl;
        std::cout << "OFFICIAL TRANSCRIPT — elearncourses.com" << std::endl;
        std::cout << std::string(60, '=') << std::endl;
        std::cout << "Student ID: " << m_id   << std::endl;
        std::cout << "Name:       " << m_name << std::endl;
        std::cout << "Program:    " << m_program << std::endl;
        std::cout << std::string(60, '-') << std::endl;

        std::cout << std::left
                  << std::setw(25) << "Subject"
                  << std::setw(10) << "Score"
                  << std::setw(8)  << "Grade"
                  << std::setw(10) << "Credits"
                  << std::setw(12) << "Semester"
                  << std::endl;
        std::cout << std::string(60, '-') << std::endl;

        for (const auto& rec : m_records) {
            std::cout << std::left
                      << std::setw(25) << rec.subject
                      << std::setw(10) << std::fixed
                      << std::setprecision(1) << rec.score
                      << std::setw(8)  << getLetterGrade(rec.score)
                      << std::setw(10) << rec.credits
                      << std::setw(12) << rec.semester
                      << std::endl;
        }

        std::cout << std::string(60, '-') << std::endl;
        std::cout << "Cumulative GPA: " << std::fixed
                  << std::setprecision(2) << calculateGPA()
                  << " / 4.00" << std::endl;
        std::cout << "Total Courses:  " << m_records.size() << std::endl;
        std::cout << std::string(60, '=') << std::endl;
    }

    // Getters
    const std::string& getId() const { return m_id; }
    const std::string& getName() const { return m_name; }
    double getGPA() const { return calculateGPA(); }
    const std::vector<CourseRecord>& getRecords() const { return m_records; }
};

class GradeManagementSystem {
private:
    std::vector<std::unique_ptr<StudentProfile>> m_students;

public:
    void addStudent(const std::string& id, const std::string& name,
                    const std::string& program) {
        // Check for duplicate ID
        auto it = std::find_if(m_students.begin(), m_students.end(),
                               [&id](const auto& s) {
                                   return s->getId() == id;
                               });
        if (it != m_students.end())
            throw std::runtime_error("Student ID " + id + " already exists");

        m_students.push_back(
            std::make_unique<StudentProfile>(id, name, program)
        );
        std::cout << "✅ Added: " << name << " (" << id << ")" << std::endl;
    }

    StudentProfile* findStudent(const std::string& id) {
        auto it = std::find_if(m_students.begin(), m_students.end(),
                               [&id](const auto& s) {
                                   return s->getId() == id;
                               });
        return (it != m_students.end()) ? it->get() : nullptr;
    }

    void displayHonorsRoll(double minGPA = 3.5) const {
        std::cout << "\n🏆 HONORS ROLL (GPA ≥ " << minGPA << ")" << std::endl;
        std::cout << std::string(40, '-') << std::endl;

        std::vector<const StudentProfile*> honors;
        for (const auto& s : m_students) {
            if (s->getGPA() >= minGPA) honors.push_back(s.get());
        }

        // Sort by GPA descending
        std::sort(honors.begin(), honors.end(),
                  [](const auto* a, const auto* b) {
                      return a->getGPA() > b->getGPA();
                  });

        int rank = 1;
        for (const auto* s : honors) {
            std::cout << rank++ << ". " << std::setw(20) << std::left
                      << s->getName()
                      << " GPA: " << std::fixed << std::setprecision(2)
                      << s->getGPA() << std::endl;
        }

        if (honors.empty())
            std::cout << "No students qualify." << std::endl;
    }

    void displayStatistics() const {
        if (m_students.empty()) {
            std::cout << "No students enrolled." << std::endl;
            return;
        }

        std::vector<double> gpas;
        for (const auto& s : m_students) gpas.push_back(s->getGPA());

        double avgGPA = std::accumulate(gpas.begin(), gpas.end(), 0.0) /
                        gpas.size();
        double maxGPA = *std::max_element(gpas.begin(), gpas.end());
        double minGPA = *std::min_element(gpas.begin(), gpas.end());

        std::cout << "\n📊 SYSTEM STATISTICS" << std::endl;
        std::cout << std::string(40, '-') << std::endl;
        std::cout << "Total Students: " << m_students.size() << std::endl;
        std::cout << "Average GPA:    " << std::fixed
                  << std::setprecision(2) << avgGPA << std::endl;
        std::cout << "Highest GPA:    " << maxGPA << std::endl;
        std::cout << "Lowest GPA:     " << minGPA << std::endl;
    }
};

int main() {
    GradeManagementSystem gms;

    try {
        // Add students
        gms.addStudent("S001", "Alice Johnson",   "Computer Science");
        gms.addStudent("S002", "Bob Smith",        "Data Science");
        gms.addStudent("S003", "Charlie Brown",    "Software Engineering");
        gms.addStudent("S004", "Diana Prince",     "Computer Science");

        // Add course records
        auto* alice = gms.findStudent("S001");
        alice->addCourse("C++ Programming",       95.0, 4, "Sem-1");
        alice->addCourse("Data Structures",       88.5, 4, "Sem-1");
        alice->addCourse("Algorithms",            92.0, 3, "Sem-1");
        alice->addCourse("Operating Systems",     85.5, 4, "Sem-2");
        alice->addCourse("Database Systems",      90.0, 3, "Sem-2");

        auto* bob = gms.findStudent("S002");
        bob->addCourse("Python Programming",      82.0, 4, "Sem-1");
        bob->addCourse("Machine Learning",        78.5, 4, "Sem-1");
        bob->addCourse("Statistics",              91.0, 3, "Sem-1");

        auto* charlie = gms.findStudent("S003");
        charlie->addCourse("JavaScript",          96.5, 4, "Sem-1");
        charlie->addCourse("Web Development",     94.0, 4, "Sem-1");
        charlie->addCourse("DevOps",              89.5, 3, "Sem-1");

        auto* diana = gms.findStudent("S004");
        diana->addCourse("C++ Programming",       72.0, 4, "Sem-1");
        diana->addCourse("Discrete Mathematics",  68.5, 3, "Sem-1");

        // Generate transcripts
        alice->generateTranscript();
        charlie->generateTranscript();

        // Display system stats
        gms.displayStatistics();
        gms.displayHonorsRoll(3.3);

    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

C++ Career Opportunities and Salaries 2025

RoleIndia (LPA)USA (USD/year)UK (GBP/year)
Junior C++ Developer₹5–10 LPA$75K–$105K£45K–£65K
C++ Software Engineer₹10–25 LPA$110K–$155K£65K–£105K
Senior C++ Engineer₹20–45 LPA$145K–$200K£95K–£150K
Game Developer (C++)₹8–30 LPA$90K–$160K£55K–£120K
Embedded Systems Engineer₹8–25 LPA$95K–$145K£55K–£100K
HFT / Quant Developer₹20–80 LPA$150K–$350K+£100K–£250K+
Systems/Kernel Engineer₹15–40 LPA$130K–$200K£85K–£145K
Also Read: C Programming Tutorial

Frequently Asked Questions — C++ Tutorial

Q1: Is C++ hard to learn for beginners? C++ has a steeper learning curve than Python or JavaScript — primarily because of manual memory management, pointers, and the complex type system. However, with a structured approach (like this tutorial), beginners make solid progress within a few months. The difficulty pays off: C++ knowledge gives you deep insight into how computers actually work.

Q2: Should I learn C or C++ first? Learn C++ directly. Modern C++ (C++11 and later) is a much safer, more productive language than C, with smart pointers, the STL, and many features that prevent common bugs. You’ll naturally encounter C concepts while learning C++ anyway.

Q3: What is the best compiler for C++ beginners? GCC (g++) on Linux/macOS or MSVC via Visual Studio on Windows are the most widely used. For learning, GCC with the -Wall -Wextra flags is excellent because it provides detailed warnings that help you learn correct practices from the start.

Q4: What is the difference between C++ and Java? C++ compiles to native machine code (faster, direct memory control, no runtime overhead). Java compiles to bytecode that runs on the JVM (platform-independent, garbage collected, slightly slower). C++ is used for systems programming and game development; Java dominates enterprise software and Android.

Q5: What is a pointer in C++ and why is it important? A pointer is a variable that stores the memory address of another variable. Pointers are important because they enable dynamic memory allocation, efficient passing of large data structures to functions, building complex data structures (linked lists, trees), and interfacing with hardware and system APIs.

Q6: What is the STL in C++? The STL (Standard Template Library) is a collection of generic, reusable data structures (vector, map, set, list, deque) and algorithms (sort, search, transform, accumulate) built into C++. It uses templates to work with any data type and is one of C++’s greatest strengths — eliminating the need to write these fundamental structures from scratch.

Q7: Is C++ still relevant in 2025? Absolutely — C++ is more relevant than ever. It’s the dominant language for game engines (Unreal Engine), high-frequency trading, operating systems components, embedded systems, browsers, and performance-critical applications. C++20 and C++23 have brought significant modernization with ranges, concepts, coroutines, and modules, keeping the language competitive and relevant for decades to come.

Conclusion — Your C++ Journey Starts Now

This comprehensive C++ tutorial has taken you through the complete landscape of C++ programming — from your first Hello, World! to advanced templates, the STL, smart pointers, modern C++20 features, and a real-world grade management system.

Here’s everything you’ve mastered in this guide:

  • Chapter 1 — Fundamentals: variables, data types, type conversions, operators
  • Chapter 2 — Control flow: if/else, switch, all loop types
  • Chapter 3 — Functions: overloading, lambdas, closures, recursion, memoization
  • Chapter 4 — Pointers and references: raw pointers, pointer arithmetic, smart pointers
  • Chapter 5 — OOP: classes, constructors, destructors, inheritance, polymorphism, abstract classes
  • Chapter 6 — Templates: function templates, class templates, variadic templates, C++20 concepts
  • Chapter 7 — STL: all major containers, algorithms, iterators
  • Chapter 8 — Exception handling and file I/O
  • Chapter 9 — Modern C++ (C++11/17/20): optional, variant, structured bindings, ranges, threads
  • Real Project — Complete Student Grade Management System

C++ is not just a programming language — it is the language that taught the world what high-performance, general-purpose programming looks like. Understanding C++ gives you insights into computer architecture, memory, performance optimization, and software design that no other language can match.

At elearncourses.com, we offer comprehensive, project-based C++ courses — from absolute beginner foundations through advanced data structures, competitive programming, game development with Unreal Engine, and systems programming. Our courses combine video instruction, hands-on coding exercises, and real-world projects to help you master C++ and launch your programming career.

Start your C++ journey today — one of the most powerful languages ever created is waiting for you.

Leave a Reply

Your email address will not be published. Required fields are marked *