Menu
Coddy logo textTech

Глубокое погружение в деструкторы

Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 24 из 104.

destructor — это специальная функция-член, которая автоматически запускается, когда объект уничтожается. Её основная задача — освобождать ресурсы, приобретённые объектом за время его существования, например динамически выделенную память, дескрипторы файлов или сетевые подключения.

destructor имеет то же имя, что и class, с префиксом в виде тильды ~. Он не принимает параметров и не имеет возвращаемого типа:

class FileHandler {
    std::string filename;
    int* buffer;
public:
    FileHandler(std::string name, size_t size) 
        : filename(name), buffer(new int[size]) {
        std::cout << "Opening " << filename << "\n";
    }
    
    ~FileHandler() {
        delete[] buffer;
        std::cout << "Closing " << filename << "\n";
    }
};

Деструкторы called автоматически в следующих ситуациях:

  • Объекты в стеке выходят из области видимости
  • Для объекта, выделенного в куче, вызывается delete
  • Время жизни временного объекта заканчивается
void example() {
    FileHandler f1("data.txt", 100);    // Вызван конструктор
    
    FileHandler* f2 = new FileHandler("log.txt", 50);
    delete f2;                           // Вызван деструктор для f2
    
}   // Вызван деструктор для f1 (выходит из области видимости)

В отличие от конструкторов, у class может быть только один destructor. Его нельзя перегружать.

Если вы не определите его, компилятор сгенерирует destructor по умолчанию, который не делает ничего особенного — он просто уничтожает каждый член. Для классов, управляющих ресурсами, вы должны написать собственный destructor, чтобы предотвратить утечки памяти.

challenge icon

Задание

Легко

Давайте создадим менеджер сессий, который отслеживает активные пользовательские сессии и демонстрирует, как деструкторы автоматически освобождают ресурсы при уничтожении объектов — независимо от того, выходят ли они из области видимости или удаляются явно.

Вы создадите два файла, чтобы организовать код:

  • Session.h: определите Session class, представляющий активную пользовательскую сессию с Dynamically выделенными данными. У вашего class должны быть:
    • Закрытые члены: username (строка), sessionId (целое число) и указатель на целочисленный массив под названием activityLog, который отслеживает действия пользователя, а также logSize для хранения размера массива
    • Конструктор, принимающий имя пользователя, идентификатор сессии и размер журнала. Он должен выделить массив журнала действий в куче, инициализировать all элементы значением 0 и вывести "Session <sessionId> started for <username>"
    • Деструктор, который освобождает выделенную память и выводит "Session <sessionId> ended for <username>"
    • Метод logActivity(int activityCode), сохраняющий код действия в следующей available позиции (текущая позиция отслеживается внутри объекта)
    • Метод getActivityCount(), возвращающий количество зарегистрированных действий
    • Метод getUsername(), возвращающий имя пользователя
  • main.cpp: продемонстрируйте работу деструктора в разных сценариях. Считайте имя пользователя и размер журнала из входных данных (каждое значение находится в отдельной строке), затем:
    • Создайте блок области видимости с помощью фигурных скобок { } и внутри него создайте размещённый в стеке Session с введённым именем пользователя, идентификатором сессии 101 и введённым размером журнала. Зарегистрируйте два действия с кодами 1 и 2, затем выведите "Stack session activities: <count>". Когда блок ends, деструктор должен запуститься automatically.
    • После блока выведите "Stack session destroyed"
    • Создайте размещённый в куче Session с помощью new, используя имя пользователя "Guest", идентификатор сессии 202 и размер журнала 3. Зарегистрируйте одно действие с кодом 5, выведите "Heap session activities: <count>", а затем явно выполните delete для него.
    • После deletion выведите "Heap session destroyed"

Это задание демонстрирует два основных способа вызова деструкторов: автоматически, когда объекты стека покидают область видимости, и вручную, когда вы удаляете объекты кучи. Оба пути должны корректно освобождать Dynamically выделенный журнал действий, чтобы предотвратить утечки памяти.

Подключите файл заголовка в main.cpp с помощью #include "Session.h".

Попробуйте сами

#include <iostream>
#include <string>
#include "Session.h"

using namespace std;

int main() {
    // Прочитать ввод
    string username;
    int logSize;
    getline(cin, username);
    cin >> logSize;
    
    // TODO: Создайте блок области видимости с фигурными скобками { }
    // Внутри блока:
    //   - Создайте Session, выделенный в стеке, с введённым username, ID сессии 101 и введённым размером лога
    //   - Зарегистрируйте две активности с кодами 1 и 2
    //   - Print "Stack session activities: <count>"
    // Когда блок заканчивается, деструктор вызывается автоматически
    
    // TODO: After the block, print "Stack session destroyed"
    
    // TODO: Создайте Session, выделенный в куче, используя new
    //   - Username: "Guest", session ID: 202, log size: 3
    //   - Зарегистрируйте одну активность с кодом 5
    //   - Print "Heap session activities: <count>"
    //   - Удалите сессию
    
    // TODO: After deletion, print "Heap session destroyed"
    
    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C++