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.
📖 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
- Install
winget install MSYS2.MSYS2 (then: pacman -S mingw-w64-ucrt-x86_64-gcc)
xcode-select --install
sudo apt install build-essential cmake gdb
- Editor: Visual Studio Code with C/C++ (Microsoft), CMake Tools.
- Create
hello.cpp#include <iostream> int main() { std::cout << "Hello, GayaGnan!" << std::endl; } - Run it and check the version
g++ hello.cpp -o hello && ./hello g++ --version
- 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
- basics
- functions
- classes
- constructors and destructors
- RAII
- references
- pointers
- STL vector, map, set, queue, stack
- templates
- algorithms
- exceptions
- smart pointers
- testing
🏗️ Mini-projects
- Sorting benchmark
- Maze solver
- Board game engine
- Command-line inventory
Projects use synthetic data only.