C++ Exercises

Practise what you've learned. Type the missing piece into each highlighted gap and press Check. Further down are programming challenges with editors and model solutions — copy them to compile and run locally.

Fill in the blanks

Basics

Include the header for console input and output.

#include <>

Print a value followed by a newline.

std:: << total << '\n';

Let the compiler deduce the type.

 price = 9.99;

A value computed at compile time.

 int board_size = 8;

Read a whole line of text.

std::(std::cin, name);

Convert an int to double before dividing.

double avg = <double>(total) / count;

Print booleans as true/false.

std::cout << std:: << ready << '\n';

Visit every element of a vector without copying.

for (const auto name : names) std::cout << name << '\n';

Functions

A parameter the function can modify.

void normalise(std::vector<double> values);

Read a large object without copying it.

int count(const std::string text);

Transfer a string's contents instead of copying.

names.push_back(std::(name));

Give a parameter a default value.

void greet(const std::string& name, int times  1);

Print a floating-point number with two decimals using a manipulator.

std::cout << std::fixed << std::(2) << price;

Containers and algorithms

Add an element to the end of a vector.

scores.(95);

Index with bounds checking.

int x = values.(3);

Check whether a map has a key (C++20).

if (stock.("apples")) { /* ... */ }

A container that keeps unique values in sorted order.

std::<std::string> tags;

A scoped enumeration.

enum  Colour { red, green, blue };

Unpack a struct with members name and age into variables.

auto [name, ] = person;

Sort a vector with the ranges library.

std::ranges::(names);

Count the elements that satisfy a lambda.

auto n = std::ranges::(v, [limit](int x) { return x > limit; });

Keep only even numbers in a view pipeline.

auto evens = numbers | std::views::([](int n) { return n % 2 == 0; });

Remove all elements matching a condition (C++20).

std::(v, [](int x) { return x < 0; });

Memory and classes

A pointer that points to nothing.

Book* found = ;

Create an object owned by a unique_ptr.

auto engine = std::<Engine>("V8");

Shared ownership of an object.

std::<Texture> grass = std::make_shared<Texture>("grass.png");

Members only the class itself can access.

class Account {
:
    double balance_ = 0;
};

A member function that doesn't modify the object.

double balance()  { return balance_; }

Prevent implicit conversion through a constructor.

 Temperature(double celsius);

The destructor of class Logger.

Logger();

Generate all comparison operators from the members.

auto operator(const Version&) const = default;

Forbid copying.

Connection(const Connection&) = ;

Derive publicly from a base class.

class SavingsAccount :  Account { /* ... */ };

Mark a function that derived classes can override.

 double area() const = 0;

Check that you really override a base-class function.

double area() const  { return w * h; }

Templates, errors and the library

Declare a function template.

 <typename T>
T largest(const T& a, const T& b);

Constrain a template to integer types.

template <std:: T>
T gcd(T a, T b);

Report an error by throwing.

 std::invalid_argument("negative price");

A value that may be absent.

std::<int> find_index(const std::vector<int>& v, int x);

Open a file for reading.

std:: in{"data.txt"};

Format a number with two decimals.

std::string s = std::("{:.2f}", price);

Programming challenges

1. Word frequencies

Count how often each word appears in a sentence and print the words with their counts, most frequent first (alphabetical for ties).

Try it yourself · main.cpp
Edit the code, copy it into main.cpp, then compile and run it with c++ -std=c++20 -Wall main.cpp -o main && ./main. How to set up a C++ compiler
Show a model solution
C++
#include <algorithm>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

int main() {
    std::istringstream text{"the cat and the hat and the bat"};
    std::map<std::string, int> counts;
    for (std::string w; text >> w;) ++counts[w];

    std::vector<std::pair<std::string, int>> sorted(counts.begin(), counts.end());
    std::ranges::stable_sort(sorted, std::greater{}, &std::pair<std::string, int>::second);
    for (const auto& [word, n] : sorted) std::cout << word << ' ' << n << '\n';
}
Output
the 3
and 2
bat 1
cat 1
hat 1

2. A bank account class

Write a BankAccount class with an owner, a balance that can never go negative, deposit and withdraw functions that throw on invalid amounts, and a transaction count.

Try it yourself · main.cpp
Edit the code, copy it into main.cpp, then compile and run it with c++ -std=c++20 -Wall main.cpp -o main && ./main. How to set up a C++ compiler
Show a model solution
C++
#include <iostream>
#include <stdexcept>
#include <string>
#include <utility>

class BankAccount {
public:
    explicit BankAccount(std::string owner) : owner_(std::move(owner)) {}

    void deposit(double amount) {
        if (amount <= 0) throw std::invalid_argument("deposit must be positive");
        balance_ += amount;
        ++transactions_;
    }
    void withdraw(double amount) {
        if (amount <= 0) throw std::invalid_argument("withdrawal must be positive");
        if (amount > balance_) throw std::runtime_error("insufficient funds");
        balance_ -= amount;
        ++transactions_;
    }
    double balance() const { return balance_; }
    int transactions() const { return transactions_; }
    const std::string& owner() const { return owner_; }

private:
    std::string owner_;
    double balance_ = 0;
    int transactions_ = 0;
};

int main() {
    BankAccount acc{"Ana"};
    acc.deposit(100);
    acc.withdraw(30);
    try {
        acc.withdraw(500);
    } catch (const std::exception& e) {
        std::cout << "error: " << e.what() << '\n';
    }
    std::cout << acc.owner() << ": " << acc.balance() << " after " << acc.transactions() << " transactions\n";
}
Output
error: insufficient funds
Ana: 70 after 2 transactions

3. Shapes with polymorphism

Create an abstract Shape with area() and name(), implement Circle and Rectangle, store several in a std::vector<std::unique_ptr<Shape>>, and print the largest.

Try it yourself · main.cpp
Edit the code, copy it into main.cpp, then compile and run it with c++ -std=c++20 -Wall main.cpp -o main && ./main. How to set up a C++ compiler
Show a model solution
C++
#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

class Shape {
public:
    virtual ~Shape() = default;
    virtual double area() const = 0;
    virtual std::string name() const = 0;
};

class Circle : public Shape {
public:
    explicit Circle(double r) : r_(r) {}
    double area() const override { return 3.14159265 * r_ * r_; }
    std::string name() const override { return "circle"; }
private:
    double r_;
};

class Rectangle : public Shape {
public:
    Rectangle(double w, double h) : w_(w), h_(h) {}
    double area() const override { return w_ * h_; }
    std::string name() const override { return "rectangle"; }
private:
    double w_, h_;
};

int main() {
    std::vector<std::unique_ptr<Shape>> shapes;
    shapes.push_back(std::make_unique<Circle>(1.5));
    shapes.push_back(std::make_unique<Rectangle>(3, 2.5));
    shapes.push_back(std::make_unique<Circle>(0.5));
    auto biggest = std::ranges::max_element(shapes, {}, [](const auto& s) { return s->area(); });
    std::cout << "largest: " << (*biggest)->name() << " with area " << (*biggest)->area() << '\n';
}
Output
largest: rectangle with area 7.5

4. A generic stack with a concept

Write a class template Stack<T> with push, pop (returning std::optional<T>) and size, and a function constrained with a concept that sums the contents of a stack of numbers.

Try it yourself · main.cpp
Edit the code, copy it into main.cpp, then compile and run it with c++ -std=c++20 -Wall main.cpp -o main && ./main. How to set up a C++ compiler
Show a model solution
C++
#include <concepts>
#include <iostream>
#include <optional>
#include <vector>

template <typename T>
class Stack {
public:
    void push(T value) { items_.push_back(std::move(value)); }
    std::optional<T> pop() {
        if (items_.empty()) return std::nullopt;
        T top = std::move(items_.back());
        items_.pop_back();
        return top;
    }
    std::size_t size() const { return items_.size(); }
private:
    std::vector<T> items_;
};

template <typename T>
concept Number = std::integral<T> || std::floating_point<T>;

template <Number T>
T drain_sum(Stack<T>& s) {
    T total{};
    while (auto v = s.pop()) total += *v;
    return total;
}

int main() {
    Stack<double> s;
    for (double x : {1.5, 2.25, 4.0}) s.push(x);
    std::cout << s.size() << " values, sum " << drain_sum(s) << ", now " << s.size() << '\n';
    std::cout << std::boolalpha << s.pop().has_value() << '\n';
}
Output
3 values, sum 7.75, now 0
false

5. Grade report from a file

Read lines of the form name,score from a file, skip bad lines, and print the average, the best student and a grade for each, using algorithms where they fit.

TXT grades.csv
Ana,82
Ben,91
oops
Chloe,77
Dev,64
Try it yourself · main.cpp
Edit the code, copy it into main.cpp, then compile and run it with c++ -std=c++20 -Wall main.cpp -o main && ./main. How to set up a C++ compiler
Show a model solution
C++
#include <algorithm>
#include <format>
#include <fstream>
#include <iostream>
#include <numeric>
#include <sstream>
#include <string>
#include <vector>

struct Result {
    std::string name;
    int score;
};

char grade(int score) {
    return score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 70 ? 'C' : score >= 60 ? 'D' : 'F';
}

int main() {
    std::ifstream in{"grades.csv"};
    std::vector<Result> results;
    for (std::string line; std::getline(in, line);) {
        std::istringstream fields{line};
        Result r;
        if (std::getline(fields, r.name, ',') && fields >> r.score) results.push_back(r);
        else std::cout << "skipping: " << line << '\n';
    }
    double avg = std::accumulate(results.begin(), results.end(), 0.0,
                                 [](double sum, const Result& r) { return sum + r.score; }) / results.size();
    auto best = std::ranges::max_element(results, {}, &Result::score);
    std::cout << std::format("average {:.1f}, best {} ({})\n", avg, best->name, best->score);
    for (const auto& r : results) std::cout << std::format("{:<6} {:>3} {}\n", r.name, r.score, grade(r.score));
}
Output
skipping: oops
average 78.5, best Ben (91)
Ana     82 B
Ben     91 A
Chloe   77 C
Dev     64 D