Мета роботи
Спостерігати створення та знищення об’єктів, передати володіння ресурсом і зіставити фактичний вивід програми зі своєю моделлю часу життя.
Що потрібно знати
Змінна має тип і значення. Функція main — точка входу програми. Фігурні дужки {} обмежують блок коду. Оператор << передає текст або значення до std::cout, стандартного потоку виводу. Якщо ці поняття незнайомі, перечитайте приклади в демонстраційній лекції.
Теоретичний мінімум
Об’єкт зберігає стан певного типу. Конструктор ініціалізує цей стан. Деструктор виконується при завершенні часу життя об’єкта. Для локальних автоматичних об’єктів це зазвичай вихід із відповідного блоку; у межах одного блоку вони знищуються у зворотному порядку створення.
Володіння та переміщення
Ownership (володіння) — відповідальність за звільнення ресурсу. std::unique_ptr — розумний вказівник з одним власником. Move semantics (семантика переміщення) дозволяє передавати ресурси між об’єктами замість їх копіювання. У цій роботі переміщується власник; керований об’єкт лишається на тому самому місці.
RAII — Resource Acquisition Is Initialization (отримання ресурсу під час ініціалізації) пов’язує ресурс із часом життя об’єкта-власника. Ручний delete у цій програмі не потрібний.
Рядок, посилання та const
std::string — тип стандартної бібліотеки для тексту. У const std::string& name символ & означає посилання: параметр звертається до переданого рядка без створення його окремої копії. const забороняє змінити рядок через це посилання. Поле name_ отримує власну копію, тому не залежить від часу життя параметра.
Підготовка середовища
Потрібен компілятор із підтримкою C++17. Використайте доступне навчальне середовище або локальний компілятор. Збережіть файл у кодуванні UTF-8. Наведені команди запускайте в терміналі з каталогу, де лежить файл.
g++ --version або clang++ --version. Скомпілюйте: g++ -std=c++17 -Wall -Wextra -pedantic main.cpp -o lifetime. Запустіть: ./lifetime. Якщо доступний лише Clang, замініть g++ на clang++. На macOS компілятор входить до Command Line Tools.cl /std:c++17 /W4 /EHsc /utf-8 main.cpp /Fe:lifetime.exe. Запуск: lifetime.exe. Звичайний термінал без налаштованого компілятора може не знайти cl.Демонстраційний приклад
Перед запуском спрогнозуйте: чи буде dynamic знищено одразу після переміщення? Чи знищиться outer раніше за inner? Запишіть свій прогноз — так вивід стане перевіркою розуміння.
Запишіть очікуваний порядок повідомлень до запуску програми.
Покрокове виконання
- Крок 1 із 4
Створіть main.cpp
Скопіюйте повний код із наступного розділу. Перевірте, що розширення файлу
.cpp, а не.cpp.txt. - Крок 2 із 4
Зберіть програму
Виконайте команду для своєї системи. Якщо компіляція не вдалася, прочитайте першу помилку й перевірте назву файлу та заголовки.
- Крок 3 із 4
Запустіть і зафіксуйте результат
Скопіюйте вивід у нотатки. Зіставте його з прогнозом та очікуваним результатом нижче.
- Крок 4 із 4
Змініть один чинник
Додайте
next.reset();після виведення порожнього власника. Повторіть збірку й поясніть, чому повідомлення про dynamic тепер з’являється раніше.
Повний код
#include <iostream>
#include <memory>
#include <string>
#include <utility>
class Trace {
public:
explicit Trace(const std::string& name) : name_(name) {
std::cout << "+ " << name_ << "\n";
}
~Trace() {
std::cout << "- " << name_ << "\n";
}
private:
std::string name_; // Об’єкт володіє власною копією назви.
};
int main() {
Trace outer("outer");
{
Trace inner("inner");
auto owner = std::make_unique<Trace>("dynamic");
auto next = std::move(owner); // Передаємо єдине володіння.
std::cout << "owner порожній: "
<< std::boolalpha << (!owner) << "\n";
} // Знищуються next, owner, inner у зворотному порядку.
std::cout << "Кінець внутрішнього блоку\n";
} // Знищується outer.
Розбір коду
Що тут відбувається?
#includeпідключає оголошення зі стандартних заголовків.<memory>потрібен для власника,<utility>— дляstd::move.explicitзабороняє неявно створювати Trace через цей конструктор.: name_(name)— список ініціалізації: поле отримує значення до входу в тіло конструктора.outerживе у блоціmain.innerживе лише у вкладеному блоці.std::make_unique<Trace>створює окремий Trace та його власника.autoвиводить типstd::unique_ptr<Trace>.std::move(owner)дозволяє перемістити володіння доnext. Конструктор Trace при цьому повторно не викликається.!ownerперевіряє, чи вказівник порожній.std::boolalphaналаштовує друк логічних значень словами true або false.- При виході з внутрішнього блоку next видаляє dynamic, порожній owner нічого не видаляє, а inner завершує життя. outer існує до кінця main.
Очікуваний результат
+ outer
+ inner
+ dynamic
owner порожній: true
- dynamic
- inner
Кінець внутрішнього блоку
- outer
На екрані термінала повинні з’явитися ці вісім рядків у наведеному порядку. Символ + позначає створення, - — знищення. Назви outer, inner, dynamic — обрані для експерименту мітки, а не ключові слова C++.
Частка завершених об’єктів
Нехай — кількість повідомлень про створення, — кількість повідомлень про знищення. Для цього контрольованого прикладу обчислимо частку :
Значення 1 узгоджується із завершенням часу життя всіх трьох об’єктів. Це пояснювальний підрахунок, не універсальний тест відсутності витоків: повідомлення програми не перевіряють усю пам’ять чи інші ресурси.
Типові помилки
Налагодження
Debugging (налагодження) — пошук причин відмінності між очікуваною й фактичною поведінкою.
Не знайдено main.cpp
Не знайдено make_unique
<memory> і ввімкнено C++17. make_unique з’явився у C++14, тому старий режим компіляції може його не підтримувати.Вивід відрізняється
Некоректно відображається український текст
Завдання для самостійної перевірки
- Додайте другий локальний
Traceу внутрішній блок. Перед запуском спрогнозуйте порядок знищення. - Додайте
next.reset();до кінця внутрішнього блоку.reset()звільняє керований об’єкт і залишає власника порожнім. Поясніть, чи буде друге повідомлення- dynamic. - Видаліть
reset()і перенесітьnextу ще один вкладений блок. Зафіксуйте зміну часу життя власника. - Коротко поясніть, чому передача власника не створює другого
Trace.
Що зафіксувати після роботи
У демонстраційному експерименті збережіть вихідний код, команду компіляції, вивід і коротке пояснення змін у кожному досліді. Офіційний формат здавання буде визначений матеріалами дисципліни.