Деструктор и правило нуля
Конструктор выполняется, когда объект создаётся. У него есть пара — метод, который выполняется, когда объект перестаёт существовать.
Представьте стопку тарелок: тарелку, поставленную последней, снимают первой. Объекты одного блока живут так же. Порядок их создания и уничтожения виден, если оба метода печатают имя объекта:
#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, без параметров и без типа результата. Он выполняется сам, когда заканчивается время жизни объекта.
Для локальной переменной это выход из блока, где она объявлена. Разберём вывод по шагам:
+a— создан объектa.+b— создан объектbво внутреннем блоке.-b— на закрывающей скобке внутреннего блокаbуничтожается.end—mainпечатает слово.-a— в концеmainуничтожаетсяa.
Объекты одного блока уничтожаются в обратном порядке, как тарелки из стопки: созданный последним уходит первым. Порядок вызова деструкторов часто спрашивают на собеседовании, и ответ один: обратный порядку создания.
Деструктор нужен, когда объект сам владеет чем-то, что надо вернуть системе, например выделенной памятью. Объект получает это в конструкторе, а возвращает в деструкторе, и забыть о возврате уже нельзя. Похоже на библиотеку с турникетом: книгу выдают на входе, а на выходе турникет забирает её сам.
Правило нуля
Большинству классов свой деструктор не нужен. Строки std::string и векторы std::vector сами освобождают свою память, а деструкторы полей вызываются автоматически, когда уничтожается объект, которому они принадлежат:
#include <string>
#include <vector>
struct Contact {
std::string name;
std::vector<std::string> phones;
};Деструктор в Contact не объявлен: компилятор создаёт его сам, и при уничтожении контакта этот деструктор вызывает деструкторы name и phones. Копирование тоже работает: копия контакта получает собственные строку и вектор.
Это правило нуля: класс, поля которого сами освобождают свою память, не объявляет своего деструктора, а копирование у него работает само. Пишите классы из таких полей, и думать о деструкторе почти никогда не придётся.
Главное
- Деструктор
~Tвыполняется сам, когда заканчивается время жизни объекта. - Локальный объект уничтожается на закрывающей скобке своего блока.
- Объекты одного блока уничтожаются в обратном порядке создания.
- Класс из полей вроде
std::stringиstd::vectorне объявляет своего деструктора: это правило нуля.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.