Практические задания по C++
Задания для самостоятельной работы после теории intro. Каждый пункт — мини-проект с критерием готовности. Подсказки "с чего начать" помогают, если застряли
полное решение собирайте сами. Самопроверка: C++ — чек-лист.
Код собирается без предупреждений (-Wall или /W4), проходит описанные проверки, вы можете устно объяснить владение памятью и границы модулей.
Не требуют внешних учебников — только компилятор, CMake и при необходимости Qt SDK / Vulkan SDK.
Уровень 1 — язык и память
1.1. Каталог файлов
Цель: обход каталога, фильтр по расширению .cpp.
Что изучить: std::filesystem (C++17), directory_iterator, path::extension().
С чего начать:
#include <filesystem>
namespace fs = std::filesystem;
for (const auto& entry : fs::recursive_directory_iterator(".")) {
if (!entry.is_regular_file()) continue;
// сравнить entry.path().extension() с ".cpp"
}
Разбор:
#include <filesystem>подключает стандартный модуль для работы с файлами и директориями на уровне C++17.namespace fs = std::filesystem;создаёт короткий псевдоним, чтобы не писать длинное имя пространства имён в каждой строке.fs::recursive_directory_iterator(".")запускает рекурсивный обход от текущей папки, включая вложенные каталоги.entry.is_regular_file()отсекает каталоги и специальные узлы, чтобы фильтрация выполнялась только по обычным файлам.entry.path().extension()возвращает расширение файла; сравнение с".cpp"формирует итоговый фильтр исходников C++.
Требования:
- вывод относительного пути и размера файла;
- при ошибке доступа — сообщение, программа продолжает обход.
Проверка: каталог с вложенными папками; пустой каталог — вежливое сообщение, без падения.
1.2. RAII-лог
Цель: класс LogScope, пишущий в файл при создании строку enter <имя> и при уничтожении leave <имя>.
Подсказка по каркасу:
class LogScope {
std::string name_;
std::ofstream& log_;
public:
LogScope(std::string name, std::ofstream& log);
~LogScope(); // записать leave
LogScope(const LogScope&) = delete;
LogScope& operator=(const LogScope&) = delete;
};
Разбор:
- Класс
LogScopeреализует паттерн RAII: вход в область фиксируется в конструкторе, выход — в деструкторе. - Поле
std::string name_хранит человекочитаемое имя текущего scope, чтобы лог был связан с конкретной частью кода. - Поле
std::ofstream& log_хранит ссылку на уже открытый поток, что исключает лишние копии и повторные открытия файла. - Запись
~LogScope();означает, что при выходе из области видимости автоматически будет вызван деструктор и записанleave. = deleteу копирующих операций защищает от двойного "владения" одним и тем же логическим scope и от некорректного дублирования записей.
Требования:
- запрет копирования;
- в
main— вложенные блоки{ LogScope a(...); { LogScope b(...); throw ... } }— для уже созданных scope строкиleaveвсё равно появляются.
Материал: Идиомы современного C++, Управление памятью в C++, Обработка исключений в C++.
1.3. Rule of Five в мини-строке
Цель: класс SmallString с буфером в куче — конструктор, копирование, перемещение, деструктор, operator=.
Минимальный API: size(), data() или c_str(), push_back(char).
Проверка: AddressSanitizer / UB-санитайзер — нет утечек при цепочке присваиваний и std::vector<SmallString> с ростом.
Уровень 2 — STL и C++20
2.1. Частотный словарь
Цель: прочитать текстовый файл, посчитать слова, вывести топ-10.
Шаги:
std::ifstream+ построчное чтение.- Разбить строку на слова (по пробелам или
std::istringstream). - Нормализация:
tolower, убрать пунктуацию у краёв слова. std::unordered_map<std::string, int>для счётчиков.- Положить пары в
vector, отсортировать по частоте.
Материал: Диапазоны и представления в C++20 для ranges-версии сортировки.
2.2. Конвейер ranges
Цель: из vector<int> — сумма квадратов чётных чисел одной цепочкой filter → transform → свёртка.
Пример направления (C++20):
auto sum = nums
| std::views::filter([](int n){ return n % 2 == 0; })
| std::views::transform([](int n){ return n * n; });
// далее std::ranges::fold_left или ручной цикл для суммы
Разбор:
- Это конвейер
ranges: данные проходят через несколько стадий без создания лишних промежуточных контейнеров. std::views::filter(...)пропускает только чётные числа по условиюn % 2 == 0.- Лямбда в
filterпринимаетint nи возвращаетbool, то есть решение "оставить элемент или отбросить". std::views::transform(...)преобразует прошедшие фильтр элементы в их квадратыn * n.- Итоговая переменная
sumв этом фрагменте содержит "представление диапазона"; для получения числа нужна отдельная свёртка (fold_leftили цикл).
Проверка: результат совпадает с обычным циклом на тех же данных.
2.3. optional и ошибки
Цель: parse_int(std::string_view) -> std::optional<int> без исключений.
Логика: пройти символы; при первом не-цифре (кроме опционального - в начале) вернуть std::nullopt.
CLI: аргументы argv[1..], для неудачных — строка invalid.
2.4. Реализация вручную (по желанию)
Цель — написать одну структуру или алгоритм своими руками, прогнать тесты и сравнить с файлом из TheAlgorithms/C-Plus-Plus.
Варианты (достаточно одного):
| Задача | Что реализовать | Файл для сравнения |
|---|---|---|
| Стек | push, pop, top на массиве | stack_using_array.cpp |
| Сортировка слиянием | vector<int> → отсортированный массив | merge_sort.cpp |
| BST | insert, contains в двоичном дереве поиска | binary_search_tree.cpp |
Проверка
- случайные тесты дают тот же результат, что и эталонный код;
- в
READMEпроекта указаны ссылка на файл репозитория и одно отличие подхода (например,std::unique_ptrвместоnew/delete— см. RAII, управление памятью).
Материалы
Уровень 3 — сборка и тесты
3.1. CMake-библиотека + exe
Цель: статическая mathlib (факториал, gcd) + demo.
Структура:
project/
├── CMakeLists.txt
├── include/mathlib.hpp
├── src/mathlib.cpp
└── src/main.cpp
CMake (скелет):
add_library(mathlib STATIC src/mathlib.cpp)
target_include_directories(mathlib PUBLIC include)
add_executable(demo src/main.cpp)
target_link_libraries(demo PRIVATE mathlib)
Разбор:
add_library(mathlib STATIC ...)создаёт статическую библиотеку, которая соберётся в отдельный артефакт и затем будет линковаться.target_include_directories(... PUBLIC include)публикует каталог заголовков для самой библиотеки и для всех зависимых целей.add_executable(demo ...)объявляет исполняемую программу, которая использует функции изmathlib.target_link_libraries(demo PRIVATE mathlib)связываетdemoс библиотекой; ключевое словоPRIVATEограничивает эту зависимость только текущей целью.- Такой шаблон разделяет код на переиспользуемый модуль и приложение, что упрощает тестирование и расширение проекта.
Материал: CMake — первая программа, Конфигурация и сборка в C++.
3.2. GTest
Цель: три теста на mathlib (gcd норма, gcd(0,x), факториал малых n), target calc_tests, ctest.
Материал: Google Test и Catch2 в C++ — скопируйте блок FetchContent + gtest_discover_tests.
Уровень 4 — UI (Qt)
4.1. Widgets
Цель: окно со списком задач — поле ввода, кнопка "добавить", QListWidget.
Подсказки:
QLineEdit+QPushButton+QListWidgetвQVBoxLayout;- в слоте кнопки:
list->addItem(lineEdit->text()); lineEdit->clear();.
Проверка: данные живут в сессии (сохранение на диск — по желанию).
Материал: Qt — первая программа.
4.2. Qt Quick
Цель: счётчик по Qt Quick — первая программа на QML + кнопка "сброс", reset() в C++ с Q_INVOKABLE.
Проверка: Label с text: "..." + counter.value обновляется без setText из C++.
Уровень 5 — графика
5.1. Треугольник Vulkan
Цель: GLFW, Vulkan SDK, validation layers, один render pass, цветной треугольник.
Не требуется: текстуры, depth, индексные буферы.
Материал: Vulkan и низкоуровневая графика на C++, Разработка игр с использованием C++.
5.2. Индексы и uniform
Цель: квад из двух треугольников, ортографическая матрица в uniform buffer, вращение по таймеру.
Уровень 6 — интеграция
6.1. Мини-сервис
Цель: консоль — конфиг (JSON/INI), HTTP GET на localhost (cpp-httplib header-only), лог в файл с RAII.
Связь: Сетевое взаимодействие в C++, Простые приложения на C++.
Как проверять себя
| Вопрос | Зачем |
|---|---|
Кто владеет каждым new / unique_ptr? | нет утечек |
| Что будет при исключении в середине функции? | RAII |
| Какой стандарт C++ в CMake? | ranges / optional |
| Сколько раз копируются данные в цепочке? | производительность |
После уровней 1–3 — C++ — чек-лист. После 4–5 — C++ — углублённые темы или /encyclopedia/9-spinoff/9-04-razrabotka-igr/intro.
Формат выполнения задания
Чтобы материал не выглядел как "просто список пунктов", для каждого задания фиксируйте одинаковый шаблон:
- Что сделал — 3-5 строк по сути.
- Код — ссылка на папку/файл задания.
- Как проверил — команды и результат.
- Что сломалось и как починил — минимум один найденный баг.
- Что улучшить дальше — 1-2 технических шага.
Этот формат превращает упражнения в портфолио инженерного прогресса.
Критерии качества решения
| Критерий | Что считается хорошим результатом |
|---|---|
| Читаемость | короткие функции, предсказуемые имена, без "магических" чисел |
| Безопасность памяти | нет сырого владения через new/delete без необходимости |
| Сборка | проект собирается на чистой машине по инструкции в README |
| Проверяемость | есть автотесты или воспроизводимая ручная проверка |
| Связность | видно связь задания с теорией из соседних статей |
Рекомендуемая траектория по времени
- Неделя 1: уровни 1.1-1.3.
- Неделя 2: уровни 2.1-2.3.
- Неделя 3: уровни 3.1-3.2.
- Неделя 4: один путь на выбор: Qt или Vulkan.
Темп можно менять, но порядок полезно сохранить: язык → STL → сборка → UI/графика.
Кейс из практики — "делал задания, но прогресс не виден"
Ситуация: выполнено много упражнений, но на собеседовании трудно показать результат.
Рабочий формат:
- отдельная папка на каждое задание (
task-1-1,task-1-2, ...); - короткий
README.mdвнутри — цель, команды запуска, что проверено; - один коммит на завершённый этап.
Мини-шаблон README:
# 2.1 Частотный словарь
- Цель: посчитать топ-10 слов
- Сборка: cmake -S . -B build && cmake --build build
- Запуск: ./build/freq input.txt
- Проверка: совпадает с эталонным выводом для sample.txt
Разбор:
- Это минимальный шаблон
README, который фиксирует не только "что написано", но и "как проверить результат". - Заголовок
# 2.1 Частотный словарьсвязывает артефакт с конкретным заданием и помогает быстро найти нужный кейс в портфолио. - Строка
Сборкадокументирует воспроизводимую команду сборки; любой проверяющий может повторить шаг без догадок. - Строка
Запускфиксирует входной формат и исполняемый файл, что снижает риск неверной интерпретации результата. - Строка
Проверказадаёт критерий готовности и переводит задачу из "написал код" в "подтвердил корректность".
Результат: задания превращаются в прозрачную линейку навыков, а не в разрозненные файлы.
Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.