Перейти к содержимому
Теория · Классы и ООП · урок 4 из 15

Деструктор и правило нуля

Конструктор выполняется, когда объект создаётся. У него есть пара — метод, который выполняется, когда объект перестаёт существовать.

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

C++
#include <iostream>

struct Tracer {
    char name;
    Tracer(char n) : name(n) { std::cout << '+' << name << ' '; }
    ~Tracer() { std::cout << '-' << name << ' '; }
};

int main() {
    Tracer a('a');
    {
        Tracer b('b');
    }
    std::cout << "end ";
}  // +a +b -b end -a

Деструктор

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

Для локальной переменной это выход из блока, где она объявлена. Разберём вывод по шагам:

  1. +a — создан объект a.
  2. +b — создан объект b во внутреннем блоке.
  3. -b — на закрывающей скобке внутреннего блока b уничтожается.
  4. end — main печатает слово.
  5. -a — в конце main уничтожается a.

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

Деструктор нужен, когда объект сам владеет чем-то, что надо вернуть системе, например выделенной памятью. Объект получает это в конструкторе, а возвращает в деструкторе, и забыть о возврате уже нельзя. Похоже на библиотеку с турникетом: книгу выдают на входе, а на выходе турникет забирает её сам.

Правило нуля

Большинству классов свой деструктор не нужен. Строки std::string и векторы std::vector сами освобождают свою память, а деструкторы полей вызываются автоматически, когда уничтожается объект, которому они принадлежат:

C++
#include <string>
#include <vector>

struct Contact {
    std::string name;
    std::vector<std::string> phones;
};

Деструктор в Contact не объявлен: компилятор создаёт его сам, и при уничтожении контакта этот деструктор вызывает деструкторы name и phones. Копирование тоже работает: копия контакта получает собственные строку и вектор.

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

Главное

  • Деструктор ~T выполняется сам, когда заканчивается время жизни объекта.
  • Локальный объект уничтожается на закрывающей скобке своего блока.
  • Объекты одного блока уничтожаются в обратном порядке создания.
  • Класс из полей вроде std::string и std::vector не объявляет своего деструктора: это правило нуля.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом