20 готовых программ на C++ с кодом: практика для новичков и не только (2026)
Лучший способ выучить C++ — писать код руками, а не только читать теорию. В этой подборке 20 готовых программ на C++ с кодом: каждая компилируется, запускается и учит конкретному навыку.
C++ остаётся основой игровых движков, высоконагруженных систем, embedded-разработки и HFT. Стандарты C++20 и C++23 сделали язык заметно удобнее, поэтому примеры написаны в современном стиле: std::string, std::vector, умные указатели, auto, range-for.
Как пользоваться статьёй:
- Идите по уровням: 1–5 для новичков, 6–10 базовый, 11–15 средний, 16–20 продвинутый.
- Сначала попробуйте написать программу сами, потом сверьтесь с кодом.
- Выполните задание «Прокачка» под каждым примером — именно там происходит рост.
Все примеры собираются командой g++ -std=c++20 -O2 main.cpp -o app (подробнее — в конце статьи).
Уровень 1: программы для новичков (1–5)
Первые пять примеров закрепляют ввод-вывод, условия, циклы и функции — базу, без которой не обойтись.
1. Консольный калькулятор
Чему учит: std::cin, switch, обработка деления на ноль, цикл до конца ввода.
#include <iostream>
int main() {
double a, b;
char op;
std::cout << "Введите выражение (например, 7 * 3): ";
while (std::cin >> a >> op >> b) {
switch (op) {
case '+': std::cout << "= " << a + b << '\n'; break;
case '-': std::cout << "= " << a - b << '\n'; break;
case '*': std::cout << "= " << a * b << '\n'; break;
case '/':
if (b == 0) std::cout << "Ошибка: деление на ноль\n";
else std::cout << "= " << a / b << '\n';
break;
default: std::cout << "Неизвестная операция: " << op << '\n';
}
std::cout << "Следующее выражение (Ctrl+D для выхода): ";
}
}Прокачка: добавьте операции % и ^ (степень через std::pow) и историю последних 5 вычислений.
2. Игра «Угадай число»
Чему учит: современная генерация случайных чисел через <random> вместо устаревшего rand(), цикл while, счётчики.
#include <iostream>
#include <random>
int main() {
std::mt19937 rng(std::random_device{}());
std::uniform_int_distribution<int> dist(1, 100);
const int secret = dist(rng);
int guess = 0, attempts = 0;
std::cout << "Я загадал число от 1 до 100. Угадайте!\n";
while (guess != secret) {
std::cout << "Ваш вариант: ";
if (!(std::cin >> guess)) return 0;
++attempts;
if (guess < secret) std::cout << "Больше!\n";
else if (guess > secret) std::cout << "Меньше!\n";
}
std::cout << "Угадали за " << attempts << " попыток!\n";
}Прокачка: поменяйте роли — пусть программа угадывает ваше число бинарным поиском максимум за 7 ходов.
3. FizzBuzz
Чему учит: оператор %, работа со строками, тернарный оператор. Классическая задача с собеседований.
#include <iostream>
#include <string>
int main() {
for (int i = 1; i <= 100; ++i) {
std::string out;
if (i % 3 == 0) out += "Fizz";
if (i % 5 == 0) out += "Buzz";
std::cout << (out.empty() ? std::to_string(i) : out) << '\n';
}
}Прокачка: вынесите правила в std::vector<std::pair<int, std::string>>, чтобы добавить «Jazz» для кратных 7 без нового if.
4. Конвертер температур
Чему учит: вынос логики в функции, форматирование вывода через <iomanip>.
#include <iomanip>
#include <iostream>
double toFahrenheit(double c) { return c * 9.0 / 5.0 + 32.0; }
double toCelsius(double f) { return (f - 32.0) * 5.0 / 9.0; }
double toKelvin(double c) { return c + 273.15; }
int main() {
double value;
char unit;
std::cout << "Введите температуру и шкалу (например, 36.6 C или 100 F): ";
std::cin >> value >> unit;
std::cout << std::fixed << std::setprecision(2);
if (unit == 'C' || unit == 'c') {
std::cout << value << " °C = " << toFahrenheit(value) << " °F = "
<< toKelvin(value) << " K\n";
} else if (unit == 'F' || unit == 'f') {
double c = toCelsius(value);
std::cout << value << " °F = " << c << " °C = " << toKelvin(c) << " K\n";
} else {
std::cout << "Поддерживаются только C и F\n";
}
}Прокачка: добавьте конвертацию валют или единиц длины и выбор режима через меню.
5. Таблица умножения
Чему учит: вложенные циклы и выравнивание столбцов с помощью std::setw.
#include <iomanip>
#include <iostream>
int main() {
int n;
std::cout << "Размер таблицы: ";
std::cin >> n;
std::cout << " ";
for (int j = 1; j <= n; ++j) std::cout << std::setw(4) << j;
std::cout << '\n';
for (int i = 1; i <= n; ++i) {
std::cout << std::setw(4) << i;
for (int j = 1; j <= n; ++j) std::cout << std::setw(4) << i * j;
std::cout << '\n';
}
}Прокачка: превратите программу в тренажёр: 10 случайных примеров, подсчёт правильных ответов и времени.
Уровень 2: базовые проекты (6–10)
Здесь появляются алгоритмы, работа с файлами, контейнеры STL и первый класс — шаг от «задачек» к настоящим программам.
6. Проверка на палиндром
Чему учит: std::getline, функции <cctype>, алгоритм std::equal и обратные итераторы.
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
bool isPalindrome(const std::string& s) {
std::string clean;
for (unsigned char ch : s)
if (std::isalnum(ch)) clean += static_cast<char>(std::tolower(ch));
return std::equal(clean.begin(), clean.begin() + clean.size() / 2, clean.rbegin());
}
int main() {
std::string line;
std::cout << "Введите строку: ";
std::getline(std::cin, line);
std::cout << (isPalindrome(line) ? "Это палиндром\n" : "Не палиндром\n");
}Прокачка: добавьте поддержку кириллицы (подсказка: std::wstring или разбор UTF-8) и поиск самого длинного палиндрома в строке.
7. Простые числа: решето Эратосфена
Чему учит: std::vector, оценка сложности алгоритма, защита от переполнения через long long.
#include <iostream>
#include <vector>
// Решето Эратосфена: все простые числа до n за O(n log log n)
std::vector<int> sieve(int n) {
std::vector<bool> isPrime(n + 1, true);
std::vector<int> primes;
for (int i = 2; i <= n; ++i) {
if (!isPrime[i]) continue;
primes.push_back(i);
for (long long j = 1LL * i * i; j <= n; j += i) isPrime[j] = false;
}
return primes;
}
int main() {
int n;
std::cout << "Найти простые числа до: ";
std::cin >> n;
auto primes = sieve(n);
for (int p : primes) std::cout << p << ' ';
std::cout << "\nВсего: " << primes.size() << '\n';
}Прокачка: сравните скорость решета с наивной проверкой делителей до √n на n = 10 000 000.
8. To-do список с сохранением в файл
Чему учит: std::ifstream / std::ofstream, меню в цикле, удаление из вектора, проверка ввода.
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
const std::string FILE_NAME = "todo.txt";
std::vector<std::string> load() {
std::vector<std::string> tasks;
std::ifstream in(FILE_NAME);
for (std::string line; std::getline(in, line);)
if (!line.empty()) tasks.push_back(line);
return tasks;
}
void save(const std::vector<std::string>& tasks) {
std::ofstream out(FILE_NAME);
for (const auto& t : tasks) out << t << '\n';
}
int main() {
auto tasks = load();
while (true) {
std::cout << "\n1) Список 2) Добавить 3) Удалить 0) Выход\n> ";
int cmd;
if (!(std::cin >> cmd) || cmd == 0) break;
std::cin.ignore();
if (cmd == 1) {
for (size_t i = 0; i < tasks.size(); ++i)
std::cout << i + 1 << ". " << tasks[i] << '\n';
if (tasks.empty()) std::cout << "Задач нет\n";
} else if (cmd == 2) {
std::string task;
std::cout << "Задача: ";
std::getline(std::cin, task);
tasks.push_back(task);
save(tasks);
} else if (cmd == 3) {
size_t idx;
std::cout << "Номер: ";
std::cin >> idx;
if (idx >= 1 && idx <= tasks.size()) {
tasks.erase(tasks.begin() + (idx - 1));
save(tasks);
} else {
std::cout << "Нет такой задачи\n";
}
}
}
}Прокачка: добавьте статус «выполнено», приоритет и дедлайн — храните задачи как struct и сохраняйте в CSV.
9. Крестики-нолики
Чему учит: std::array, структурирование игровой логики, проверка победы через таблицу линий.
#include <array>
#include <iostream>
using Board = std::array<char, 9>;
void print(const Board& b) {
for (int i = 0; i < 9; i += 3)
std::cout << ' ' << b[i] << " | " << b[i + 1] << " | " << b[i + 2]
<< (i < 6 ? "\n---+---+---\n" : "\n");
}
char winner(const Board& b) {
const int lines[8][3] = {{0,1,2},{3,4,5},{6,7,8},{0,3,6},
{1,4,7},{2,5,8},{0,4,8},{2,4,6}};
for (auto& l : lines)
if (b[l[0]] != ' ' && b[l[0]] == b[l[1]] && b[l[1]] == b[l[2]])
return b[l[0]];
return ' ';
}
int main() {
Board b;
b.fill(' ');
char player = 'X';
for (int turn = 0; turn < 9; ++turn) {
print(b);
int cell;
std::cout << "Игрок " << player << ", клетка (1-9): ";
if (!(std::cin >> cell)) return 0;
if (cell < 1 || cell > 9 || b[cell - 1] != ' ') {
std::cout << "Недопустимый ход\n";
--turn;
continue;
}
b[cell - 1] = player;
if (winner(b) != ' ') {
print(b);
std::cout << "Победил " << player << "!\n";
return 0;
}
player = (player == 'X') ? 'O' : 'X';
}
print(b);
std::cout << "Ничья!\n";
}Прокачка: напишите непобедимого бота на алгоритме минимакс.
10. Банковский счёт (ООП)
Чему учит: классы, инкапсуляция, конструктор со списком инициализации, const-методы, исключения.
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
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("Сумма должна быть > 0");
balance_ += amount;
history_.push_back("+" + std::to_string(amount));
}
void withdraw(double amount) {
if (amount > balance_) throw std::runtime_error("Недостаточно средств");
balance_ -= amount;
history_.push_back("-" + std::to_string(amount));
}
double balance() const { return balance_; }
const std::string& owner() const { return owner_; }
const std::vector<std::string>& history() const { return history_; }
private:
std::string owner_;
double balance_;
std::vector<std::string> history_;
};
int main() {
BankAccount acc("Иван", 1000);
try {
acc.deposit(500);
acc.withdraw(200);
acc.withdraw(5000); // выбросит исключение
} catch (const std::exception& e) {
std::cout << "Ошибка: " << e.what() << '\n';
}
std::cout << acc.owner() << ", баланс: " << acc.balance() << '\n';
for (const auto& op : acc.history()) std::cout << " " << op << '\n';
}Прокачка: храните деньги в копейках (long long) вместо double — так делают в реальных финансовых системах. Добавьте наследника SavingsAccount с начислением процентов.
Уровень 3: средний (11–15)
На этом уровне вы работаете с ассоциативными контейнерами, лямбдами, симуляциями и замером производительности.
11. Частотный анализ текста
Чему учит: std::map, сортировка с лямбда-компаратором, перенаправление ввода из файла.
#include <algorithm>
#include <cctype>
#include <iostream>
#include <map>
#include <string>
#include <vector>
int main() {
std::map<std::string, int> freq;
std::string word;
// Чтение до конца ввода: ./app < text.txt
while (std::cin >> word) {
std::string clean;
for (unsigned char ch : word)
if (std::isalpha(ch)) clean += static_cast<char>(std::tolower(ch));
if (!clean.empty()) ++freq[clean];
}
std::vector<std::pair<std::string, int>> items(freq.begin(), freq.end());
std::sort(items.begin(), items.end(),
[](const auto& a, const auto& b) { return a.second > b.second; });
std::cout << "Топ-10 слов:\n";
for (size_t i = 0; i < items.size() && i < 10; ++i)
std::cout << items[i].first << " — " << items[i].second << '\n';
}Прокачка: замените std::map на std::unordered_map, замерьте разницу на большой книге и добавьте список стоп-слов.
12. Шифр Цезаря
Чему учит: арифметика символов, остаток от отрицательных чисел, изменение строки по ссылке в range-for.
#include <iostream>
#include <string>
std::string caesar(const std::string& text, int shift) {
shift = ((shift % 26) + 26) % 26; // поддержка отрицательного сдвига
std::string result = text;
for (char& ch : result) {
if (ch >= 'a' && ch <= 'z') ch = 'a' + (ch - 'a' + shift) % 26;
else if (ch >= 'A' && ch <= 'Z') ch = 'A' + (ch - 'A' + shift) % 26;
}
return result;
}
int main() {
std::string text;
int shift;
std::cout << "Текст (латиница): ";
std::getline(std::cin, text);
std::cout << "Сдвиг: ";
std::cin >> shift;
std::string encrypted = caesar(text, shift);
std::cout << "Шифр: " << encrypted << '\n';
std::cout << "Расшифровка: " << caesar(encrypted, -shift) << '\n';
}Прокачка: взломайте шифр без ключа частотным анализом (в английском чаще всего встречается «e») и реализуйте шифр Виженера.
13. Игра «Жизнь» Конвея
Чему учит: двумерные массивы, constexpr, клеточные автоматы, анимация в терминале через ANSI-коды и std::this_thread::sleep_for.
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
using Grid = std::vector<std::vector<int>>;
constexpr int W = 30, H = 15;
int neighbours(const Grid& g, int y, int x) {
int count = 0;
for (int dy = -1; dy <= 1; ++dy)
for (int dx = -1; dx <= 1; ++dx) {
if (dy == 0 && dx == 0) continue;
int ny = (y + dy + H) % H, nx = (x + dx + W) % W; // тор
count += g[ny][nx];
}
return count;
}
Grid step(const Grid& g) {
Grid next(H, std::vector<int>(W, 0));
for (int y = 0; y < H; ++y)
for (int x = 0; x < W; ++x) {
int n = neighbours(g, y, x);
next[y][x] = (n == 3 || (g[y][x] && n == 2)) ? 1 : 0;
}
return next;
}
int main() {
Grid g(H, std::vector<int>(W, 0));
// Планер
g[1][2] = g[2][3] = g[3][1] = g[3][2] = g[3][3] = 1;
for (int gen = 0; gen < 60; ++gen) {
std::cout << "\x1b[H\x1b[2J"; // очистка экрана
for (auto& row : g) {
for (int c : row) std::cout << (c ? '#' : '.');
std::cout << '\n';
}
g = step(g);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}Прокачка: загружайте стартовую фигуру из файла и перенесите отрисовку в окно через SFML или raylib.
14. Сортировки и бинарный поиск с замером времени
Чему учит: реализация классических алгоритмов, шаблонная функция-таймер, std::chrono. На 20 000 элементах пузырёк работает сотни миллисекунд, а quick sort и std::sort — около 1 мс: разница между O(n²) и O(n log n) видна наглядно.
#include <algorithm>
#include <chrono>
#include <iostream>
#include <random>
#include <vector>
void bubbleSort(std::vector<int>& a) {
for (size_t i = 0; i < a.size(); ++i)
for (size_t j = 0; j + 1 < a.size() - i; ++j)
if (a[j] > a[j + 1]) std::swap(a[j], a[j + 1]);
}
void quickSort(std::vector<int>& a, int lo, int hi) {
if (lo >= hi) return;
int pivot = a[(lo + hi) / 2], i = lo, j = hi;
while (i <= j) {
while (a[i] < pivot) ++i;
while (a[j] > pivot) --j;
if (i <= j) std::swap(a[i++], a[j--]);
}
quickSort(a, lo, j);
quickSort(a, i, hi);
}
int binarySearch(const std::vector<int>& a, int target) {
int lo = 0, hi = static_cast<int>(a.size()) - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (a[mid] == target) return mid;
if (a[mid] < target) lo = mid + 1; else hi = mid - 1;
}
return -1;
}
template <typename F>
long long measure(F f) {
auto start = std::chrono::steady_clock::now();
f();
auto end = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
}
int main() {
std::mt19937 rng(42);
std::vector<int> data(20000);
for (int& x : data) x = rng() % 100000;
auto a = data, b = data, c = data;
std::cout << "Пузырёк: " << measure([&] { bubbleSort(a); }) << " мс\n";
std::cout << "Quick sort: " << measure([&] { quickSort(b, 0, (int)b.size() - 1); }) << " мс\n";
std::cout << "std::sort: " << measure([&] { std::sort(c.begin(), c.end()); }) << " мс\n";
int target = c[12345];
std::cout << "Бинарный поиск " << target << " -> индекс " << binarySearch(c, target) << '\n';
}Прокачка: добавьте merge sort и heap sort, сравните время на уже отсортированном массиве.
15. Парсер CSV и отчёт по продажам
Чему учит: std::stringstream, аргументы командной строки, structured bindings (C++17), устойчивость к битым данным. Такие утилиты для обработки файлов часто пишут на реальной работе.
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
// Формат sales.csv:
// date,category,amount
// 2026-01-05,Книги,1200.50
std::vector<std::string> split(const std::string& line, char sep = ',') {
std::vector<std::string> parts;
std::stringstream ss(line);
for (std::string item; std::getline(ss, item, sep);) parts.push_back(item);
return parts;
}
int main(int argc, char* argv[]) {
std::ifstream in(argc > 1 ? argv[1] : "sales.csv");
if (!in) { std::cerr << "Не удалось открыть файл\n"; return 1; }
std::map<std::string, double> totals;
std::string line;
std::getline(in, line); // пропускаем заголовок
int lineNo = 1;
while (std::getline(in, line)) {
++lineNo;
auto cols = split(line);
if (cols.size() != 3) { std::cerr << "Строка " << lineNo << " пропущена\n"; continue; }
try {
totals[cols[1]] += std::stod(cols[2]);
} catch (...) {
std::cerr << "Строка " << lineNo << ": неверная сумма\n";
}
}
double grand = 0;
std::cout << std::fixed << std::setprecision(2);
for (const auto& [category, sum] : totals) {
std::cout << std::left << std::setw(15) << category << sum << '\n';
grand += sum;
}
std::cout << "Итого: " << grand << '\n';
}Прокачка: добавьте группировку по месяцам, поддержку полей в кавычках с запятыми внутри и экспорт отчёта в JSON.
Уровень 4: продвинутый (16–20)
Шаблоны, move-семантика, многопоточность, графы и парсеры — темы, которые спрашивают на собеседованиях на Middle C++ разработчика.
16. Шаблонный стек и проверка скобок
Чему учит: шаблоны классов, variadic templates, perfect forwarding, std::move, практическое применение стека.
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
template <typename T>
class Stack {
public:
void push(const T& value) { data_.push_back(value); }
void push(T&& value) { data_.push_back(std::move(value)); }
template <typename... Args>
void emplace(Args&&... args) { data_.emplace_back(std::forward<Args>(args)...); }
T pop() {
if (empty()) throw std::out_of_range("Стек пуст");
T top = std::move(data_.back());
data_.pop_back();
return top;
}
const T& top() const {
if (empty()) throw std::out_of_range("Стек пуст");
return data_.back();
}
bool empty() const { return data_.empty(); }
size_t size() const { return data_.size(); }
private:
std::vector<T> data_;
};
// Применение: проверка сбалансированности скобок
bool balanced(const std::string& s) {
Stack<char> st;
for (char c : s) {
if (c == '(' || c == '[' || c == '{') st.push(c);
else if (c == ')' || c == ']' || c == '}') {
if (st.empty()) return false;
char open = st.pop();
if ((c == ')' && open != '(') || (c == ']' && open != '[') ||
(c == '}' && open != '{')) return false;
}
}
return st.empty();
}
int main() {
Stack<std::string> words;
words.emplace("мир");
words.push("привет");
while (!words.empty()) std::cout << words.pop() << ' ';
std::cout << '\n';
for (std::string s : {"{[()]}", "([)]", "(()"})
std::cout << s << " -> " << (balanced(s) ? "OK" : "ошибка") << '\n';
}Прокачка: перепишите стек на сыром массиве с ручным управлением памятью и реализуйте «правило пяти» (копирование, перемещение, деструктор).
17. Многопоточное суммирование
Чему учит: std::thread, деление работы на чанки, захват переменных в лямбде, join. Каждый поток пишет в свою ячейку, поэтому мьютекс не нужен.
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <iostream>
#include <numeric>
#include <thread>
#include <vector>
// Параллельная сумма большого массива
int main() {
const size_t N = 100'000'000;
std::vector<int> data(N, 1);
unsigned threads = std::max(1u, std::thread::hardware_concurrency());
std::vector<std::int64_t> partial(threads, 0);
std::vector<std::thread> pool;
auto start = std::chrono::steady_clock::now();
size_t chunk = N / threads;
for (unsigned t = 0; t < threads; ++t) {
size_t from = t * chunk;
size_t to = (t == threads - 1) ? N : from + chunk;
pool.emplace_back([&, t, from, to] {
partial[t] = std::accumulate(data.begin() + from, data.begin() + to, std::int64_t{0});
});
}
for (auto& th : pool) th.join();
std::int64_t total = std::accumulate(partial.begin(), partial.end(), std::int64_t{0});
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start).count();
std::cout << "Потоков: " << threads << "\nСумма: " << total
<< "\nВремя: " << ms << " мс\n";
}Прокачка: перепишите на std::async / std::future, затем на std::reduce(std::execution::par, ...) и сравните с однопоточной версией. Собирайте с флагом -pthread.
18. LRU-кэш
Чему учит: комбинация std::list + std::unordered_map для операций за O(1), splice, std::optional, if с инициализатором. Одна из самых популярных задач на собеседованиях.
#include <iostream>
#include <list>
#include <optional>
#include <string>
#include <unordered_map>
template <typename K, typename V>
class LRUCache {
public:
explicit LRUCache(size_t capacity) : capacity_(capacity) {}
std::optional<V> get(const K& key) {
auto it = index_.find(key);
if (it == index_.end()) return std::nullopt;
items_.splice(items_.begin(), items_, it->second); // в начало, O(1)
return it->second->second;
}
void put(const K& key, const V& value) {
if (auto it = index_.find(key); it != index_.end()) {
it->second->second = value;
items_.splice(items_.begin(), items_, it->second);
return;
}
if (items_.size() == capacity_) {
index_.erase(items_.back().first); // вытесняем самый старый
items_.pop_back();
}
items_.emplace_front(key, value);
index_[key] = items_.begin();
}
private:
size_t capacity_;
std::list<std::pair<K, V>> items_;
std::unordered_map<K, typename std::list<std::pair<K, V>>::iterator> index_;
};
int main() {
LRUCache<int, std::string> cache(2);
cache.put(1, "один");
cache.put(2, "два");
cache.get(1); // 1 становится самым свежим
cache.put(3, "три"); // вытесняет 2
for (int k : {1, 2, 3}) {
auto v = cache.get(k);
std::cout << k << ": " << (v ? *v : "нет в кэше") << '\n';
}
}Прокачка: сделайте кэш потокобезопасным через std::mutex и добавьте время жизни записей (TTL).
19. Кратчайший путь: алгоритм Дейкстры
Чему учит: представление графа списком смежности, std::priority_queue с std::greater<>, восстановление пути. Алгоритм лежит в основе навигаторов и сетевой маршрутизации.
#include <algorithm>
#include <iostream>
#include <limits>
#include <queue>
#include <vector>
struct Edge { int to; int weight; };
using Graph = std::vector<std::vector<Edge>>;
const int INF = std::numeric_limits<int>::max();
std::vector<int> dijkstra(const Graph& g, int src, std::vector<int>& prev) {
std::vector<int> dist(g.size(), INF);
prev.assign(g.size(), -1);
using Item = std::pair<int, int>; // (расстояние, вершина)
std::priority_queue<Item, std::vector<Item>, std::greater<>> pq;
dist[src] = 0;
pq.push({0, src});
while (!pq.empty()) {
auto [d, u] = pq.top();
pq.pop();
if (d > dist[u]) continue;
for (const auto& [v, w] : g[u])
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
prev[v] = u;
pq.push({dist[v], v});
}
}
return dist;
}
int main() {
Graph g(6);
auto addEdge = [&](int a, int b, int w) { g[a].push_back({b, w}); g[b].push_back({a, w}); };
addEdge(0, 1, 7); addEdge(0, 2, 9); addEdge(0, 5, 14);
addEdge(1, 2, 10); addEdge(1, 3, 15); addEdge(2, 3, 11);
addEdge(2, 5, 2); addEdge(3, 4, 6); addEdge(4, 5, 9);
std::vector<int> prev;
auto dist = dijkstra(g, 0, prev);
for (int v = 0; v < (int)g.size(); ++v) {
std::vector<int> path;
for (int x = v; x != -1; x = prev[x]) path.push_back(x);
std::reverse(path.begin(), path.end());
std::cout << "0 -> " << v << ": " << dist[v] << " путь: ";
for (int x : path) std::cout << x << ' ';
std::cout << '\n';
}
}Прокачка: читайте граф из файла и реализуйте A* для поиска пути в лабиринте на сетке.
20. Интерпретатор математических выражений
Чему учит: парсер рекурсивного спуска, приоритет операций, правая ассоциативность степени, обработка ошибок через исключения. Это первый шаг к собственному языку программирования. Пример: 2+3*4 даёт 14, 2^3^2 — 512.
#include <cctype>
#include <cmath>
#include <iostream>
#include <stdexcept>
#include <string>
// Рекурсивный спуск:
// expr = term { ('+' | '-') term }
// term = factor { ('*' | '/') factor }
// factor = power [ '^' factor ]
// power = number | '(' expr ')' | '-' power
class Parser {
public:
explicit Parser(std::string s) : s_(std::move(s)) {}
double parse() {
double v = expr();
skip();
if (pos_ != s_.size()) throw std::runtime_error("Лишний символ: " + std::string(1, s_[pos_]));
return v;
}
private:
std::string s_;
size_t pos_ = 0;
void skip() { while (pos_ < s_.size() && std::isspace((unsigned char)s_[pos_])) ++pos_; }
bool match(char c) { skip(); if (pos_ < s_.size() && s_[pos_] == c) { ++pos_; return true; } return false; }
double expr() {
double v = term();
while (true) {
if (match('+')) v += term();
else if (match('-')) v -= term();
else return v;
}
}
double term() {
double v = factor();
while (true) {
if (match('*')) v *= factor();
else if (match('/')) {
double d = factor();
if (d == 0) throw std::runtime_error("Деление на ноль");
v /= d;
} else return v;
}
}
double factor() {
double base = power();
if (match('^')) return std::pow(base, factor()); // правоассоциативно
return base;
}
double power() {
if (match('-')) return -power();
if (match('(')) {
double v = expr();
if (!match(')')) throw std::runtime_error("Ожидалась ')'");
return v;
}
skip();
size_t start = pos_;
while (pos_ < s_.size() && (std::isdigit((unsigned char)s_[pos_]) || s_[pos_] == '.')) ++pos_;
if (start == pos_) throw std::runtime_error("Ожидалось число");
return std::stod(s_.substr(start, pos_ - start));
}
};
int main() {
std::string line;
std::cout << "calc> ";
while (std::getline(std::cin, line)) {
if (line == "exit") break;
try {
std::cout << Parser(line).parse() << '\n';
} catch (const std::exception& e) {
std::cout << "Ошибка: " << e.what() << '\n';
}
std::cout << "calc> ";
}
}Прокачка: добавьте переменные (x = 5), функции sin, sqrt и построение AST вместо немедленного вычисления.
Как скомпилировать и запустить примеры
Все 20 программ собираются стандартом C++20 без предупреждений при флагах -Wall -Wextra.
| Платформа | Команда |
|---|---|
| Linux / macOS (GCC) | g++ -std=c++20 -O2 -Wall main.cpp -o app && ./app |
| macOS (Clang) | clang++ -std=c++20 -O2 main.cpp -o app && ./app |
| Windows (MSVC) | cl /std:c++20 /EHsc main.cpp |
| Без установки | онлайн-компиляторы Compiler Explorer (godbolt.org) или OnlineGDB |
Для программы 17 добавьте флаг -pthread. Если в консоли Windows кириллица превращается в «кракозябры», выполните chcp 65001 перед запуском.
Часто задаваемые вопросы
С какой программы начать, если я новичок?
С калькулятора и «Угадай число»: они закрепляют ввод-вывод, условия и циклы за один вечер.
Какой стандарт C++ учить в 2026 году?
Ориентируйтесь на C++20 — его полностью поддерживают GCC, Clang и MSVC. Возможности C++23 (std::print, std::expected) изучайте вторым шагом.
Сколько времени займёт прохождение всех 20 программ?
При 1–2 часах в день — около месяца, включая задания «Прокачка».
Можно ли добавить эти проекты в портфолио?
Да, если доработаете их: выполните «Прокачку», добавьте CMake, юнит-тесты (GoogleTest или Catch2) и README, затем выложите на GitHub. Лучше всего смотрятся LRU-кэш, Дейкстра и интерпретатор.
Заключение
20 программ из этой статьи ведут от первого std::cin до многопоточности и парсеров — это готовый план практики на месяц. Не копируйте код вслепую: ломайте его, меняйте и расширяйте.
Сохраните статью в закладки и напишите в комментариях, какая программа далась сложнее всего.




