Современный C++
Все объяснения, задачи и лабораторная в одном месте.
Можно сохранить страницу в PDF через печать браузера. Для печати разборы ответов раскрываются автоматически.
1. Value semantics, ссылки и move
Объяснение
Значение владеет своим состоянием; ссылка обозначает существующий объект. Move разрешает перенос ресурсов из объекта, но не гарантирует перенос для любого типа. После перемещения объект остаётся допустимым, однако его содержимое часто не определено контрактом.
Задача для самостоятельного решения
Можно ли рассчитывать, что перемещённая строка всегда пустая?
Показать разбор ответа
Нет. Её можно уничтожить или присвоить новое значение, но предположение о пустоте непереносимо, если конкретный контракт этого не обещает.
2. RAII и умные указатели
Объяснение
RAII связывает ресурс со временем жизни объекта. Деструктор освобождает ресурс при обычном выходе и раскрутке стека исключением. unique_ptr выражает единственного владельца, shared_ptr — совместное владение.
Задача для самостоятельного решения
Почему цикл shared_ptr может удерживать объекты навсегда?
Показать разбор ответа
Каждый объект увеличивает счётчик другого. Один из обратных указателей должен быть невладеющим weak_ptr, иначе счётчики не станут нулевыми.
3. STL, ranges и алгоритмы
Объяснение
STL-алгоритмы отделяют операцию от контейнера через итераторы. Изменение контейнера может инвалидировать итераторы. ranges позволяют описывать преобразования, но представления часто ссылаются на исходные данные.
Задача для самостоятельного решения
Что произойдёт с итератором vector после push_back с перераспределением?
Показать разбор ответа
Он станет недействительным вместе со ссылками и указателями на прежнее хранилище. Получите итератор заново; reserve может сократить перераспределения, но не заменяет анализ времени жизни.
4. Шаблоны, concepts и generic design
Объяснение
Шаблон создаёт код для параметров типов; concept задаёт проверяемые требования к операциям. Требование наличия оператора не доказывает его математические свойства. Диагностика constraints лучше ошибки глубоко в реализации.
Задача для самостоятельного решения
Достаточно ли наличия operator< для корректной сортировки?
Показать разбор ответа
Нет: отношение должно удовлетворять строгому слабому порядку. Противоречивый компаратор нарушает предпосылки алгоритма даже при успешной компиляции.
5. Потоки, atomics и memory model
Объяснение
Data race возникает при несинхронизированном конфликтующем доступе к общей памяти. Atomic обеспечивает свойства отдельной операции, но не обязательно инварианта нескольких полей. Порядок памяти определяет видимость связанных записей.
Задача для самостоятельного решения
Два атомарных поля balance и reserved обновляются отдельно. Гарантирует ли это согласованный снимок?
Показать разбор ответа
Нет: читатель может увидеть новое balance и старое reserved. Для общего инварианта нужны mutex, версия снимка или специально доказанный алгоритм.
6. Профилирование, UB и инструменты качества
Объяснение
Undefined behavior позволяет компилятору предполагать невозможность некоторых ситуаций. Измерение скорости некорректной программы не имеет смысла. Профилирование должно предшествовать усложнению реализации.
Задача для самостоятельного решения
Как проверить подозрение на use-after-free?
Показать разбор ответа
Создайте минимальный воспроизводимый тест, выполните сборку с AddressSanitizer и символами, исправьте время жизни, затем повторите исходный сценарий и измерение производительности.
Лабораторная работа
Подготовка
Компилятор C++17 или новее. Сохраните main.cpp и соберите c++ -std=c++17 -Wall -Wextra main.cpp -o lab.
Учебный пример
#include <algorithm>
#include <iostream>
#include <memory>
#include <vector>
int main() {
auto values = std::make_unique<std::vector<int>>(std::initializer_list<int>{3,1,2});
std::sort(values->begin(), values->end());
for (int x : *values) std::cout << x << ' ';
}Как работает пример и что ожидать
Вывод 1 2 3. unique_ptr освобождает vector автоматически, vector управляет своим буфером. В этом примере обычный локальный vector был бы проще; дополнительный unique_ptr нужен здесь для наблюдения владения, а не как обязательный стиль.
Итоговая работа
Сделайте библиотеку очереди задач с RAII-ресурсами, тестом исключения и ограниченной параллельностью. Объясните необходимость каждого shared_ptr. Проверьте гонки доступным инструментом и измерьте накладные расходы на той же нагрузке.
Проверка результата
1. Опишите исходные данные и условия запуска, чтобы другой человек мог повторить работу.
2. Приложите результат обычного сценария и сравните его с ожидаемым.
3. Проверьте неверный вход, граничный случай и отказ зависимости, если она есть.
4. Объясните выбранное решение и известное ограничение.
5. Сохраните исправления после самопроверки вместе с примером, который раньше не работал.
Как оценить работу
По каждому пункту поставьте 0 (не выполнено), 1 (выполнено с пробелами) или 2 (результат воспроизводим и объяснён). Если обязательный сценарий не работает, вернитесь к нему независимо от общей суммы. Это рубрика самопроверки: сайт не исполняет присланный код и не выдаёт автоматическую оценку проекта.