Навчальний простір / C++
Об’єктно-орієнтоване програмування

Дослідження часу життя об’єктів у C++

Демонстраційна лабораторна: підготувати компілятор C++17, запустити програму, простежити деструктори та передачу володіння.

Зміст матеріалу

Мета роботи

Спостерігати створення та знищення об’єктів, передати володіння ресурсом і зіставити фактичний вивід програми зі своєю моделлю часу життя.

Що потрібно знати

Змінна має тип і значення. Функція 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.

Демонстраційний приклад

Перед запуском спрогнозуйте: чи буде dynamic знищено одразу після переміщення? Чи знищиться outer раніше за inner? Запишіть свій прогноз — так вивід стане перевіркою розуміння.

План експериментуОберіть елемент, щоб побачити пояснення

Запишіть очікуваний порядок повідомлень до запуску програми.

Покрокове виконання

Крок за кроком
  1. Крок 1 із 4

    Створіть main.cpp

    Скопіюйте повний код із наступного розділу. Перевірте, що розширення файлу .cpp, а не .cpp.txt.

1 / 4

Повний код

main.cpp cpp
#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.

Розбір коду

Що тут відбувається?

  1. #include підключає оголошення зі стандартних заголовків. <memory> потрібен для власника, <utility> — для std::move.
  2. explicit забороняє неявно створювати Trace через цей конструктор. : name_(name) — список ініціалізації: поле отримує значення до входу в тіло конструктора.
  3. outer живе у блоці main. inner живе лише у вкладеному блоці.
  4. std::make_unique<Trace> створює окремий Trace та його власника. auto виводить тип std::unique_ptr<Trace>.
  5. std::move(owner) дозволяє перемістити володіння до next. Конструктор Trace при цьому повторно не викликається.
  6. !owner перевіряє, чи вказівник порожній. std::boolalpha налаштовує друк логічних значень словами true або false.
  7. При виході з внутрішнього блоку next видаляє dynamic, порожній owner нічого не видаляє, а inner завершує життя. outer існує до кінця main.

Очікуваний результат

text
+ outer
+ inner
+ dynamic
owner порожній: true
- dynamic
- inner
Кінець внутрішнього блоку
- outer

На екрані термінала повинні з’явитися ці вісім рядків у наведеному порядку. Символ + позначає створення, - — знищення. Назви outer, inner, dynamic — обрані для експерименту мітки, а не ключові слова C++.

Частка завершених об’єктів

Нехай NcN_c — кількість повідомлень про створення, NdN_d — кількість повідомлень про знищення. Для цього контрольованого прикладу обчислимо частку rr:

r=NdNc=33=1(1)r = \frac{N_d}{N_c} = \frac{3}{3} = 1 \tag{1}

Значення 1 узгоджується із завершенням часу життя всіх трьох об’єктів. Це пояснювальний підрахунок, не універсальний тест відсутності витоків: повідомлення програми не перевіряють усю пам’ять чи інші ресурси.

Типові помилки

Налагодження

Debugging (налагодження) — пошук причин відмінності між очікуваною й фактичною поведінкою.

Не знайдено main.cpp
Перевірте поточний каталог і назву файлу. Термінал та редактор можуть працювати в різних каталогах.
Не знайдено make_unique
Переконайтеся, що підключено <memory> і ввімкнено C++17. make_unique з’явився у C++14, тому старий режим компіляції може його не підтримувати.
Вивід відрізняється
Збережіть файл, повторно зберіть та запустіть щойно створений виконуваний файл. Порівняйте розташування дужок і наявність reset().
Некоректно відображається український текст
Перевірте кодування UTF-8 у файлі й терміналі; для MSVC використайте /utf-8. Це проблема відображення тексту, а не володіння ресурсом.

Завдання для самостійної перевірки

  1. Додайте другий локальний Trace у внутрішній блок. Перед запуском спрогнозуйте порядок знищення.
  2. Додайте next.reset(); до кінця внутрішнього блоку. reset() звільняє керований об’єкт і залишає власника порожнім. Поясніть, чи буде друге повідомлення - dynamic.
  3. Видаліть reset() і перенесіть next у ще один вкладений блок. Зафіксуйте зміну часу життя власника.
  4. Коротко поясніть, чому передача власника не створює другого Trace.

Що зафіксувати після роботи

У демонстраційному експерименті збережіть вихідний код, команду компіляції, вивід і коротке пояснення змін у кожному досліді. Офіційний формат здавання буде визначений матеріалами дисципліни.

Перевірка результату

Контрольні запитання

Перевірте розуміння
Скільки разів має з’явитися «- dynamic», якщо додати next.reset() перед кінцем внутрішнього блоку?
Чому inner знищується раніше за outer?
Час життя inner завершується при виході з внутрішнього блоку. outer належить зовнішньому блоку main та існує до його завершення.

Корисні джерела

Як оформити та здати роботу →

Опрацювали матеріал?

Збережіть цей крок у своєму прогресі.

← Повернутися до дисципліни
© 2026 Анастасія Іскандарова-МалаЕлектронний навчальний посібник · ДДТУ

Пошук у посібнику

Спробуйте «C++», «RAII» або «бази даних».