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
| Domain | Examples |
|---|---|
| Game Development | Unreal Engine, Unity (core), Call of Duty, GTA |
| Operating Systems | Windows (core), parts of Linux/macOS |
| Browsers | Chrome (V8 engine), Firefox (Gecko) |
| Databases | MySQL, MongoDB, SQLite |
| Embedded Systems | Automotive ECUs, robotics, IoT devices |
| High-Frequency Trading | Sub-microsecond trading algorithms |
| Scientific Computing | NASA systems, physics simulations |
| Compilers/Tools | LLVM, 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
- Download from https://visualstudio.microsoft.com
- Select “Desktop development with C++”
- Option B: Install MinGW-w64 (GCC for Windows) + VS Code
On macOS:
# Install Xcode Command Line Tools (includes Clang)
xcode-select --install
# Verify installation
clang++ --version
g++ --versionOn Linux (Ubuntu/Debian):
sudo apt update
sudo apt install build-essential g++ gdb cmake -y
g++ --versionStep 2: Choose Your IDE or Editor
| Tool | Best For | Cost |
|---|---|---|
| Visual Studio | Windows, full-featured IDE | Free (Community) |
| CLion | Cross-platform, JetBrains | Paid (free for students) |
| VS Code + C++ Extension | Lightweight, all platforms | Free |
| Code::Blocks | Beginners, lightweight | Free |
| Xcode | macOS development | Free |
Step 3: Write and Compile Your First Program
// 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
}# 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.cppUnderstanding the compilation process:
Source Code (.cpp) → Preprocessor → Compiler → Assembler → Linker → Executable
hello.cpp → (macros) → (.o file) → (assembly) → (libraries) → helloChapter 1: C++ Fundamentals
Variables, Data Types, and Constants
#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
#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
#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
#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;
}// 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.
#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++
// 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)
#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
#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)
#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
// 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
| Role | India (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.