👑 Premium Sign in Sign up

Language lab · Intermediate

➕ C++

Concept → trace → write code → run tests → explain why → improve → mini-project. Start in the browser if you like; set up your own computer when you are ready.

💻 Runs on your computerOfficial download & docs ↗

📖 Topics with real code (16)

Every verified output below was produced by running the code with g++ (GCC) 14.4.0 on 5 Nov 2026. Open a topic to edit the code and run it right here in your browser.

🌱 Beginner (5)

Variables and data types

C++ types: int, long long, double, bool, char and std::string. auto lets the compiler infer the type. static_cast converts safely between types.

#include <iostream>
#include <string>

int main() {
    int age = 15;
    long long population = 1'428'000'000LL;
    double height = 1.62;
    bool isStudent = true;
    char grade = 'A';
    std::string name = "Asha";
    auto city = std::string("Hyderabad");
    std::cout << name << ' ' << age << ' ' << height << ' ' << std::boolalpha << isStudent << ' ' << grade << ' ' << city << '\n';
    std::cout << "population " << population << ", sizeof(int) " << sizeof(int) << '\n';
    int marks = std::stoi("88") + 2;
    double avg = static_cast<double>(7) / 2;
    std::cout << marks << ' ' << avg << ' ' << 7 / 2 << ' ' << static_cast<int>(9.99) << '\n';
}

✅ Verified output

Asha 15 1.62 true A Hyderabad
population 1428000000, sizeof(int) 4
90 3.5 3 9
Operators and boolean logic

Arithmetic, comparison and logical operators work like C. bool prints as 1/0 unless you use std::boolalpha. Compound operators (+=, *=) and ++ change a variable in place.

#include <iostream>

int main() {
    int a = 17, b = 5;
    std::cout << a + b << ' ' << a - b << ' ' << a * b << ' ' << a / b << ' ' << a % b << '\n';
    bool hasTicket = true, isVip = false;
    std::cout << std::boolalpha << "enter: " << (hasTicket && !isVip) << " either: " << (hasTicket || isVip) << '\n';
    int x = 5;
    x += 3; x *= 2; ++x;
    std::cout << "x=" << x << " 6&3=" << (6 & 3) << " 1<<4=" << (1 << 4) << '\n';
    std::cout << "17 >= 5 is " << (a >= b) << '\n';
}

✅ Verified output

22 12 85 3 2
enter: true either: true
x=17 6&3=2 1<<4=16
17 >= 5 is true
Conditionals: if, else if, switch

if with an initialiser (C++17) keeps a variable local to the if. switch matches integer or char values; remember break.

#include <iostream>
#include <string>

int main() {
    int marks = 78;
    std::string grade;
    if (marks >= 90) grade = "A+";
    else if (marks >= 75) grade = "A";
    else if (marks >= 35) grade = "Pass";
    else grade = "Fail";
    std::cout << "Grade: " << grade << '\n';
    if (int bonus = marks / 10; bonus > 5) std::cout << "bonus " << bonus << '\n';
    char day = 's';
    switch (day) {
        case 's': std::cout << "Weekend\n"; break;
        default:  std::cout << "School day\n";
    }
    std::cout << (marks >= 35 ? "passed" : "try again") << '\n';
}

✅ Verified output

Grade: A
bonus 7
Weekend
passed
Loops: for, range-for, while

The range-based for loop walks every element of a container. break and continue work as in C.

#include <iostream>
#include <vector>
#include <iomanip>

int main() {
    for (int i = 1; i <= 5; i++) {
        if (i == 3) continue;
        std::cout << "for " << i << '\n';
    }
    int n = 10;
    while (n > 0) {
        if (n < 4) break;
        std::cout << "while " << n << '\n';
        n -= 3;
    }
    std::vector<std::string> colours{"red", "green"};
    for (const auto& c : colours) std::cout << "each " << c << '\n';
    for (int r = 1; r <= 3; r++) {
        for (int c = 1; c <= 3; c++) std::cout << std::setw(3) << r * c;
        std::cout << '\n';
    }
}

✅ Verified output

for 1
for 2
for 4
for 5
while 10
while 7
while 4
each red
each green
  1  2  3
  2  4  6
  3  6  9
Functions: defaults, overloading and references

Default arguments, overloading by parameter type, and & references that let a function change the caller's variable without pointers.

#include <iostream>
#include <string>
#include <vector>

int area(int length, int width = 1) { return length * width; }
double area(double radius) { return 3.14159 * radius * radius; }
void addBonus(int& marks, int bonus = 5) { marks += bonus; }
double average(const std::vector<int>& v) {
    int s = 0;
    for (int x : v) s += x;
    return static_cast<double>(s) / v.size();
}

int main() {
    std::cout << area(5, 3) << ' ' << area(7) << ' ' << area(1.0) << '\n';
    int m = 80;
    addBonus(m);
    addBonus(m, 10);
    std::cout << "marks " << m << ", average " << average({4, 9, 2, 7}) << '\n';
}

✅ Verified output

15 7 3.14159
marks 95, average 5.5

🌿 Intermediate (7)

std::vector and algorithms

vector is a growing array. <algorithm> gives sort, find, count_if, transform and more; lambdas say what to do.

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

int main() {
    std::vector<int> marks{72, 95, 64, 88};
    marks.push_back(91);
    std::sort(marks.begin(), marks.end());
    for (int m : marks) std::cout << m << ' ';
    std::cout << "| first " << marks.front() << " last " << marks.back() << " size " << marks.size() << '\n';
    auto above80 = std::count_if(marks.begin(), marks.end(), [](int m) { return m > 80; });
    int total = std::accumulate(marks.begin(), marks.end(), 0);
    std::cout << "above 80: " << above80 << ", total " << total << '\n';
    std::vector<int> sq(5);
    std::iota(sq.begin(), sq.end(), 1);
    std::transform(sq.begin(), sq.end(), sq.begin(), [](int x) { return x * x; });
    for (int x : sq) std::cout << x << ' ';
    std::cout << '\n';
}

✅ Verified output

64 72 88 91 95 | first 64 last 95 size 5
above 80: 3, total 410
1 4 9 16 25
std::string

std::string manages its own memory. Use +, find, substr, replace and std::to_string; std::reverse works on strings too.

#include <iostream>
#include <string>
#include <algorithm>
#include <sstream>
#include <cctype>

int main() {
    std::string t = "GayaGnan learning";
    std::string up = t;
    std::transform(up.begin(), up.end(), up.begin(), [](unsigned char c) { return std::toupper(c); });
    std::cout << up << " | length " << t.size() << '\n';
    t.replace(t.find("learning"), 8, "labs");
    std::cout << t << " | first word: " << t.substr(0, t.find(' ')) << '\n';
    std::istringstream in("the cat and the hat");
    std::string w;
    int count = 0;
    while (in >> w) count++;
    std::cout << "words " << count << ", " << "total " + std::to_string(49.5 * 3) << '\n';
    std::string p = "level", r(p.rbegin(), p.rend());
    std::cout << "palindrome: " << std::boolalpha << (p == r) << '\n';
}

✅ Verified output

GAYAGNAN LEARNING | length 17
GayaGnan labs | first word: GayaGnan
words 5, total 148.500000
palindrome: true
std::map, unordered_map and set

map keeps keys sorted, unordered_map is a hash table (fast, unordered), set keeps unique sorted items.

#include <iostream>
#include <map>
#include <unordered_map>
#include <set>
#include <sstream>

int main() {
    std::map<std::string, int> stock{{"pen", 40}, {"book", 12}};
    stock["eraser"] = 25;
    stock["pen"] -= 5;
    for (const auto& [item, count] : stock) std::cout << item << ' ' << count << '\n';
    std::cout << "has ruler? " << stock.count("ruler") << '\n';
    std::unordered_map<std::string, int> freq;
    std::istringstream in("the cat and the hat and the bat");
    for (std::string w; in >> w;) freq[w]++;
    std::cout << "the=" << freq["the"] << " and=" << freq["and"] << '\n';
    std::set<int> uniq{5, 3, 5, 1, 3};
    for (int x : uniq) std::cout << x << ' ';
    std::cout << '\n';
}

✅ Verified output

book 12
eraser 25
pen 35
has ruler? 0
the=3 and=2
1 3 5
Classes, constructors and encapsulation

A class has private data and public methods. The constructor initialises members; const methods promise not to change the object.

#include <iostream>
#include <string>
#include <stdexcept>

class BankAccount {
public:
    BankAccount(std::string owner, double balance = 0) : owner_(std::move(owner)), balance_(balance) {}
    void deposit(double amount) {
        if (amount <= 0) throw std::invalid_argument("amount must be positive");
        balance_ += amount;
    }
    double balance() const { return balance_; }
    const std::string& owner() const { return owner_; }
private:
    std::string owner_;
    double balance_;
};

int main() {
    BankAccount acc("Asha", 500);
    acc.deposit(250);
    std::cout << acc.owner() << ' ' << acc.balance() << '\n';
}

✅ Verified output

Asha 750
Inheritance, virtual functions and polymorphism

virtual lets a base-class pointer call the right derived method at run time. Use override to catch mistakes, and a virtual destructor in base classes.

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

struct Shape {
    virtual ~Shape() = default;
    virtual double area() const = 0;
    virtual const char* name() const = 0;
};
struct Circle : Shape {
    double r;
    explicit Circle(double r) : r(r) {}
    double area() const override { return M_PI * r * r; }
    const char* name() const override { return "Circle"; }
};
struct Rect : Shape {
    double w, h;
    Rect(double w, double h) : w(w), h(h) {}
    double area() const override { return w * h; }
    const char* name() const override { return "Rect"; }
};

int main() {
    std::vector<std::unique_ptr<Shape>> shapes;
    shapes.push_back(std::make_unique<Circle>(1));
    shapes.push_back(std::make_unique<Rect>(3, 4));
    for (const auto& s : shapes) std::cout << s->name() << ' ' << s->area() << '\n';
}

✅ Verified output

Circle 3.14159
Rect 12
Exceptions

throw raises an exception; try/catch handles it by type. Standard ones live in <stdexcept>. Destructors still run while the stack unwinds (RAII).

#include <iostream>
#include <stdexcept>
#include <string>

struct Guard {
    ~Guard() { std::cout << "cleanup ran\n"; }
};
double divide(double a, double b) {
    if (b == 0) throw std::domain_error("division by zero");
    return a / b;
}

int main() {
    try {
        Guard g;
        std::cout << divide(10, 2) << '\n';
        std::cout << divide(1, 0) << '\n';
    } catch (const std::domain_error& e) {
        std::cout << "caught: " << e.what() << '\n';
    }
    try {
        std::stoi("abc");
    } catch (const std::invalid_argument&) {
        std::cout << "not a number\n";
    }
}

✅ Verified output

5
cleanup ran
caught: division by zero
not a number
Files with fstream

ofstream writes and ifstream reads. The file closes automatically when the stream object goes out of scope.

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cstdio>

int main() {
    const char* path = "/tmp/marks.csv";
    {
        std::ofstream out(path);
        out << "name,marks\nAsha,88\nRavi,95\n";
    }
    std::ifstream in(path);
    std::string line, name, marks;
    std::getline(in, line);
    int total = 0, count = 0;
    while (std::getline(in, line)) {
        std::istringstream row(line);
        std::getline(row, name, ',');
        std::getline(row, marks);
        std::cout << name << " -> " << marks << '\n';
        total += std::stoi(marks);
        count++;
    }
    std::remove(path);
    std::cout << "average " << static_cast<double>(total) / count << '\n';
}

✅ Verified output

Asha -> 88
Ravi -> 95
average 91.5

🌳 Advanced (4)

Recursion and memoization

A recursive function with an unordered_map cache. constexpr can even compute some results at compile time.

#include <iostream>
#include <unordered_map>
#include <vector>

std::unordered_map<int, unsigned long long> memo;
unsigned long long fib(int n) {
    if (n < 2) return n;
    if (auto it = memo.find(n); it != memo.end()) return it->second;
    return memo[n] = fib(n - 1) + fib(n - 2);
}
constexpr int factorial(int n) { return n <= 1 ? 1 : n * factorial(n - 1); }
int binarySearch(const std::vector<int>& a, int t, int lo, int hi) {
    if (lo > hi) return -1;
    int mid = lo + (hi - lo) / 2;
    if (a[mid] == t) return mid;
    return a[mid] < t ? binarySearch(a, t, mid + 1, hi) : binarySearch(a, t, lo, mid - 1);
}

int main() {
    for (int i = 0; i < 10; i++) std::cout << fib(i) << ' ';
    std::cout << "| fib(80) = " << fib(80) << '\n';
    static_assert(factorial(5) == 120, "computed at compile time");
    std::vector<int> a{2, 5, 8, 12, 16, 23, 38};
    std::cout << "index of 23: " << binarySearch(a, 23, 0, (int)a.size() - 1) << '\n';
}

✅ Verified output

0 1 1 2 3 5 8 13 21 34 | fib(80) = 23416728348467685
index of 23: 5
Templates and lambdas

A template works for any type that supports the operations it uses. Lambdas capture variables by value [=] or by reference [&].

#include <iostream>
#include <vector>
#include <string>
#include <functional>

template <typename T>
T maxOf(const std::vector<T>& v) {
    T best = v.front();
    for (const T& x : v) if (x > best) best = x;
    return best;
}
template <typename F, typename T>
T applyTwice(F f, T x) { return f(f(x)); }

int main() {
    std::cout << maxOf<int>({4, 9, 2}) << ' ' << maxOf<std::string>({"kiwi", "apple", "mango"}) << '\n';
    std::cout << applyTwice([](int n) { return n * 3; }, 2) << ' ' << applyTwice([](std::string s) { return s + "!"; }, std::string("hi")) << '\n';
    int count = 0;
    auto counter = [&count]() { return ++count; };
    counter(); counter();
    std::function<int(int)> square = [](int x) { return x * x; };
    std::cout << "count " << count << ", square(7) " << square(7) << '\n';
}

✅ Verified output

9 mango
18 hi!!
count 2, square(7) 49
Threads, mutex and futures

std::thread runs work in parallel; std::mutex with lock_guard protects shared data; std::async returns a future for a result.

#include <iostream>
#include <thread>
#include <mutex>
#include <future>
#include <vector>

int main() {
    long total = 0;
    std::mutex m;
    std::vector<std::thread> workers;
    for (int t = 0; t < 4; t++)
        workers.emplace_back([&] {
            for (int i = 0; i < 1000; i++) {
                std::lock_guard<std::mutex> lock(m);
                total++;
            }
        });
    for (auto& w : workers) w.join();
    std::cout << "total " << total << '\n';
    auto f1 = std::async(std::launch::async, [] { return 6 * 7; });
    auto f2 = std::async(std::launch::async, [] { return 10 * 10; });
    std::cout << "futures " << f1.get() << ' ' << f2.get() << '\n';
}

✅ Verified output

total 4000
futures 42 100
Smart pointers and RAII

unique_ptr owns an object alone and deletes it automatically; shared_ptr counts owners. No manual delete means no leaks: this is RAII.

#include <iostream>
#include <memory>
#include <string>

struct Sensor {
    std::string id;
    explicit Sensor(std::string i) : id(std::move(i)) { std::cout << "open " << id << '\n'; }
    ~Sensor() { std::cout << "close " << id << '\n'; }
};

int main() {
    {
        auto a = std::make_unique<Sensor>("temp-1");
        auto b = std::move(a);                     // ownership moves
        std::cout << "a empty: " << std::boolalpha << (a == nullptr) << '\n';
    }                                              // closed here automatically
    auto s1 = std::make_shared<Sensor>("gps-1");
    {
        auto s2 = s1;
        std::cout << "owners " << s1.use_count() << '\n';
    }
    std::cout << "owners " << s1.use_count() << '\n';
    std::weak_ptr<Sensor> w = s1;
    std::cout << "weak alive: " << !w.expired() << '\n';
}

✅ Verified output

open temp-1
a empty: true
close temp-1
open gps-1
owners 2
owners 1
weak alive: true
close gps-1

🛠️ Set up and verify

  1. Install
    winget install MSYS2.MSYS2   (then: pacman -S mingw-w64-ucrt-x86_64-gcc)
    xcode-select --install
    sudo apt install build-essential cmake gdb
  2. Editor: Visual Studio Code with C/C++ (Microsoft), CMake Tools.
  3. Create hello.cpp
    #include <iostream>
    
    int main() {
        std::cout << "Hello, GayaGnan!" << std::endl;
    }
  4. Run it and check the version
    g++ hello.cpp -o hello && ./hello
    g++ --version
  5. Health check: paste what your terminal printed.

Install one language at a time. These commands only install or print versions; nothing here deletes files. Versions change: check the official page and note the version you installed.

🧭 The 14-lab path

  • 00 · Setup and verify — install the runtime and editor, run hello world, print the version, fix PATH
  • 01 · Syntax and trace — follow each line and the value of every variable
  • 02 · Logic — conditionals and decision trees
  • 03 · Loops — trace tables and loop challenges
  • 04 · Functions — break problems down and test each piece
  • 05 · Data structures — lists, maps, stacks, queues and trees you can see
  • 06 · Debugging — bug cases with a hint ladder
  • 07 · Algorithms — search, sort and Big-O comparison
  • 08 · Files and data — read and write fixture files
  • 09 · API or database — talk to a mock API or a sandbox database
  • 10 · Testing — write tests and read failing assertions
  • 11 · Security — secure coding and dependency hygiene for this language
  • 12 · Project — one portfolio mini-project
  • 13 · Assessment — quiz, coding test, rubric and an explain-your-answer prompt

📚 Modules

  1. basics
  2. functions
  3. classes
  4. constructors and destructors
  5. RAII
  6. references
  7. pointers
  8. STL vector, map, set, queue, stack
  9. templates
  10. algorithms
  11. exceptions
  12. smart pointers
  13. testing

🏗️ Mini-projects

  • Sorting benchmark
  • Maze solver
  • Board game engine
  • Command-line inventory

Projects use synthetic data only.