What is C++?
Introduction
C++ is a compiled, general‑purpose programming language created by Bjarne Stroustrup in 1985 as an extension of the C language. It adds object‑orientation, generic programming, low‑level memory control and a massive standard library.
Key Characteristics
- Compiled – source code is turned into native machine code.
- Statically typed – type checking is done at compile time.
- Supports multiple paradigms – procedural, OOP, generic, functional.
- Performance‑critical – fine‑grained control over memory and resources.
Hello World – Your First C++ Program
Save the following as hello.cpp, compile it, and run it.
#include <iostream> int main() { std::cout << "Hello, World!" << std::endl; return 0; }
Compile and run (Linux/macOS/macOS, Windows‑WSL, or MinGW):
g++ -std=c++20 hello.cpp -o hello
./hello # prints “Hello, World!”
Install a C++ compiler (GCC, Clang or MSVC), write the program above, compile it and verify the output.
Setting up the Compiler
Choosing a Toolchain
For learning you can pick any of these:
- GCC – the GNU Compiler Collection (Linux/macOS/WSL).
- Clang – LLVM‑based, great diagnostics.
- MSVC – the Microsoft Visual C++ compiler (Windows).
- Online IDEs – Compiler Explorer, OnlineGDB.
Verifying Installation
g++ --version
clang++ --version
cl
VS Code with the C/C++ extension or JetBrains CLion give you IntelliSense, debugging, and project management out of the box.
Compiling with Flags
Common useful flags:
-Wall -Wextra -Wpedantic– enable most warnings.-O2– optimisation level.-std=c++20– select the language standard.-g– include debug symbols forgdb/lldb.
Create a simple project with two source files (main.cpp and utils.cpp),
compile them together, and run the resulting executable.
Variables & Data Types
Fundamental Types
| Type | Size (typical) | Example |
|---|---|---|
bool |
1 byte | bool ok = true; |
char |
1 byte | char c = 'A'; |
int |
4 bytes | int i = 42; |
long / long long |
8 bytes | long long big = 123456789LL; |
float |
4 bytes | float f = 3.14f; |
double |
8 bytes | double d = 2.71828; |
Signed vs Unsigned
Appending u makes an integer type unsigned (unsigned int or
uint32_t). Unsigned types cannot represent negative values, which can be useful for
bit‑wise operations.
Type Modifiers
Keywords that affect size and sign:
shortlong,long longsigned(default forint)unsigned
Variable Declaration & Initialisation
int count = 0; // initialise to zero double price = 19.99; char grade = 'A'; bool isReady = true; unsigned int mask = 0xFF; long bigNum = 123456789L;
Write a program that declares one variable of each primitive type, prints their size using
sizeof,
and then swaps the values of two integer variables without using a temporary variable.
Control Flow
If / else
int score = 85; if (score >= 90) { std::cout << "Grade A" << std::endl; } else if (score >= 80) { std::cout << "Grade B" << std::endl; } else { std::cout << "Needs improvement" << std::endl; }
Switch (C++17 foldable switch)
char choice = 'b'; switch (choice) { case 'a': case 'b': case 'c': std::cout << "Option a–c selected" << std::endl; break; default: std::cout << "Other option" << std::endl; }
Loops
for (init; condition; increment)while (condition)do { … } while (condition);- Range‑based
for (auto &v : container)
int sum = 0; for (int i = 1; i <= 5; ++i) { sum += i; } std::cout << "Sum = " << sum << std::endl;
Write a program that prints the first 20 Fibonacci numbers using a while loop.
Functions
Function Syntax
General form: return_type name(parameter_list) { body }
int add(int a, int b) { return a + b; } double average(const std::vector<int>& values) { int sum = 0; for (int v : values) sum += v; return static_cast<double>(sum) / values.size(); }
Function Overloading
Same name, different parameter types/counts.
int max(int a, int b) { return (a > b) ? a : b; } double max(double a, double b) { return (a > b) ? a : b; }
Default Arguments
Supply a value that is used when the caller omits that argument.
void greet(const std::string& name = "World") {
std::cout << "Hello, " << name << "!\n";
}
Implement a factorial function (recursive) for int
and an overloaded version that works with unsigned long long. Write a short driver that
calls both.
Object‑Oriented Programming
Class Declaration
class Person { private: std::string name; int age; public: Person(const std::string& name, int age) : name(name), age(age) {} const std::string& getName() const { return name; } int getAge() const { return age; } void birthday() { ++age; } };
Inheritance
class Employee : public Person { private: std::string department; public: Employee(const std::string& name, int age, const std::string& dept) : Person(name, age), department(dept) {} const std::string& getDept() const { return department; } };
Polymorphism (virtual functions)
class Shape { public: virtual double area() const = 0; virtual ~Shape() {} }; class Circle : public Shape { double radius; public: Circle(double r) : radius(r) {} double area() const override { return 3.14159 * radius * radius; } }; int main() { std::vector<std::unique_ptr<Shape>> shapes; shapes.push_back(std::make_unique<Circle>(2.5)); for (const auto& s : shapes) { std::cout << "Area = " << s->area() << std::endl; } }
Model a simple library system: a base class Item with id and
title,
then derive Book and Magazine. Override a virtual printInfo()
method for each derived class.
Standard Library Containers
Why use containers?
They provide generic, memory‑safe collections with well‑defined complexity guarantees.
Sequence containers
std::vector<T>– dynamic array, contiguous memory.std::list<T>– doubly‑linked list.std::deque<T>– double‑ended queue.std::forward_list<T>– singly‑linked list (C++11+).
Associative containers
std::set<T>– ordered unique keys.std::map<Key, Value>– ordered key/value pairs.std::unordered_set<T>– hash‑based set (C++11+).std::unordered_map<Key, Value>– hash table.
Basic usage examples
#include <vector> #include <iostream> int main() { std::vector<int> numbers = { 1, 2, 3 }; numbers.push_back(4); for (int n : numbers) { std::cout << n << ' '; } std::cout << std::endl; }
#include <map> #include <string> #include <iostream> int main() { std::map<std::string, int> scores; scores["Alice"] = 95; scores["Bob"] = 87; for (const auto& kv : scores) { std::cout << kv.first << ": " << kv.second << std::endl; } }
Create a program that reads a list of words from the console, stores them in a
std::unordered_set, and then reports how many unique words were entered.
Templates & Generics
Function templates
template<typename T> void swapValues(T& a, T& b) { T tmp = a; a = b; b = tmp; } int main() { int x = 5, y = 10; swapValues(x, y); // works for ints std::string s1 = "foo", s2 = "bar"; swapValues(s1, s2); // works for std::string }
Class templates
template<typename T, size_t N> class SimpleVector { T data[N]; public: constexpr size_t size() const { return N; } T& operator[](size_t i) { return data[i]; } const T& operator[](size_t i) const { return data[i]; } };
Template specialization
Provide a custom implementation for a particular type.
template<>
struct std::hash<MyType> {
size_t operator()(const MyType& obj) const noexcept { … }
};
Write a generic Stack<T> class with push, pop and
top operations.
Then specialise it for bool so that it stores bits compactly (you can use
std::vector<bool> for simplicity).
Modern C++ (C++11 +)
Auto type deduction
auto x = 42; // int
auto y = 3.14; // double
auto z = std::vector<int>{1,2,3}; // deduced type
Range‑based for loop
std::vector<int> v = {1,2,3};
for (auto& n : v) {
n *= 2;
}
Smart pointers
#include <memory> #include <iostream> struct Node { int value; std::unique_ptr<Node> next; Node(int v) : value(v) {} }; int main() { auto head = std::make_unique<Node>(1); head->next = std::make_unique<Node>(2); std::cout << head->value << ", " << head->next->value << std::endl; }
Lambda expressions
auto add = [](int a, int b) { return a + b; };
std::cout << add(3,4) << '\n';
constexpr
Compile‑time constants and functions.
constexpr int square(int x) { return x*x; }
static_assert(square(5) == 25);
Write a program that uses a std::vector<int> of random numbers, then sorts them
with std::sort using a lambda comparator that orders them in descending order.
Exception Handling
try / catch / finally
C++ has try, catch and throw. The finally pattern is
emulated with RAII.
#include <stdexcept> #include <iostream> double divide(double a, double b) { if (b == 0.0) throw std::runtime_error("Division by zero"); return a / b; } int main() { try { std::cout << divide(10, 2) << std::endl; std::cout << divide(5, 0) << std::endl; } catch (const std::runtime_error& e) { std::cerr << "Error: " << e.what() << std::endl; } }
Custom exception types
class InvalidAgeException : public std::runtime_error {
public:
InvalidAgeException(const std::string& msg) : std::runtime_error(msg) {}
};
Implement a BankAccount class with deposit, withdraw and
balance.
Throw a custom InsufficientFundsException when a withdrawal would result in a negative
balance and handle it in main.
Concurrency
Thread basics
C++11 introduced std::thread, std::mutex and related primitives.
#include <thread> #include <iostream> void worker(int id) { for (int i = 0; i < 5; ++i) { std::cout << "Thread " << id << ": " << i << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(200)); } } int main() { std::thread t1(worker, 1); std::thread t2(worker, 2); t1.join(); t2.join(); }
Mutex & lock_guard
std::mutex m;
{
std::lock_guard<std::mutex> lock(m);
// critical section
}
Thread pool (high‑level)
Standard C++ does not ship a thread pool, but you can build one with std::async or use a
third‑party library.
#include <future> #include <iostream> int fib(int n) { if (n <= 1) return n; return fib(n-1) + fib(n-2); } int main() { auto fut = std::async(std::launch::async, fib, 30); std::cout << "Computing…" << std::endl; int result = fut.get(); // blocks until fib finishes std::cout << "Result = " << result << std::endl; }
Write a producer‑consumer example using a std::queue protected by a
std::mutex and a std::condition_variable. One thread should push numbers
1‑100 into the queue, the other should pop and print them.
Capstone Project – Console To‑Do List
Implement a **single‑file** C++ console program that lets the user manage a simple To‑Do list.
Feature checklist
- Store tasks as objects (
struct Task { int id; std::string text; bool done; }). - Add, toggle (complete/incomplete), delete, and list tasks.
- Persist the list to a text file (
tasks.txt) on exit and load it on start. - Use
std::vector,std::fstream,std::getline, and basic RAII. - Handle I/O errors with exceptions.
- Optional: colour the “done” tasks using ANSI escape codes.
Starter skeleton
#include <iostream> #include <vector> #include <fstream> #include <sstream> #include <string> struct Task { int id; std::string text; bool done; Task(int i, std::string t) : id(i), text(std::move(t)), done(false) {} std::string serialize() const { return std::to_string(id) + "," + text + "," + (done ? "1" : "0"); } static Task deserialize(const std::string& line) { std::istringstream ss(line); std::string part; int i; std::getline(ss, part, ','); i = std::stoi(part); std::getline(ss, part, ','); std::string txt = part; std::getline(ss, part, ','); bool d = (part == "1"); Task t(i, txt); t.done = d; return t; } }; class TodoApp { static constexpr const char* FILE_NAME = "tasks.txt"; std::vector<Task> tasks; int nextId = 1; void load() { std::ifstream in(FILE_NAME); if (!in) return; std::string line; while (std::getline(in, line)) { Task t = Task::deserialize(line); tasks.push_back(t); if (t.id >= nextId) nextId = t.id + 1; } } void save() { std::ofstream out(FILE_NAME, std::ios::trunc); for (const auto& t : tasks) { out << t.serialize() << \n; } } void printMenu() { std::cout << "\n--- To‑Do List ---\n"; for (const auto& t : tasks) { std::cout << t.id << ". [" << (t.done ? "x" : " ") << "] " << t.text << \n; } std::cout << "\n(a)dd, (t)oggle, (d)elete, (q)uit: "; } void addTask(const std::string& txt) { tasks.push_back(Task(nextId++, txt)); } void toggleTask(int id) { for (auto& t : tasks) { if (t.id == id) { t.done = !t.done; break; } } } void deleteTask(int id) { tasks.erase(std::remove_if(tasks.begin(), tasks.end(), [id](const Task& t){ return t.id == id; }), tasks.end()); } public: TodoApp() { load(); } ~TodoApp() { save(); } void run() { while (true) { printMenu(); std::string cmd; std::getline(std::cin, cmd); if (cmd.empty()) continue; char c = std::tolower(cmd[0]); if (c == 'q') break; switch (c) { case 'a': std::cout << "Task description: "; std::getline(std::cin, cmd); addTask(cmd); break; case 't': std::cout << "ID to toggle: "; std::getline(std::cin, cmd); toggleTask(std::stoi(cmd)); break; case 'd': std::cout << "ID to delete: "; std::getline(std::cin, cmd); deleteTask(std::stoi(cmd)); break; default: std::cout << "Unknown command\n"; } } } }; int main() { TodoApp app; app.run(); return 0; }
What you’ll learn
- Classes, structs and encapsulation.
- Standard containers (
vector) and algorithms. - File I/O with
fstreamand error handling. - Basic interactive console UI.
- RAII – resources are automatically released.
After this capstone you’ll be ready to move on to:
- GUI programming (Qt, wxWidgets, SFML).
- Modern C++ libraries (Boost, fmt, ranges).
- Build systems (CMake, Meson).
- Testing frameworks (GoogleTest, Catch2).
Extend the app with a priority field (enum LOW/MEDIUM/HIGH) and allow sorting by priority. Add colour output (ANSI escape codes) so completed tasks appear in grey.