Деструктор, правило нуля и const-методы
Конструктор выполняется, когда объект создаётся. У него есть пара — метод, который выполняется, когда объект перестаёт существовать. Порядок этих вызовов видно, если оба печатают имя объекта:
#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Деструктор
Деструктор (destructor) — метод с именем класса и тильдой впереди, например ~Tracer, без параметров и без типа результата. Он выполняется сам, когда заканчивается время жизни объекта. Для локальной переменной это выход из блока, где она объявлена: b уничтожается на закрывающей скобке внутреннего блока, а a — в конце main. Объекты одного блока уничтожаются в обратном порядке: созданный последним — первым.
Деструктор нужен, когда объект сам владеет ресурсом, который надо вернуть системе, например выделенной памятью. Ресурс получают в конструкторе, а возвращают в деструкторе, и забыть о возврате уже нельзя.
Правило нуля
Большинству классов свой деструктор не нужен. Строки std::string и векторы std::vector сами освобождают свою память, а деструкторы полей вызываются автоматически, когда уничтожается объект, которому они принадлежат:
#include <string>
#include <vector>
struct Contact {
std::string name;
std::vector<std::string> phones;
};Деструктор в Contact не объявлен, но при уничтожении контакта компилятор вызывает деструкторы name и phones. Копирование тоже работает: копия контакта получает собственные строку и вектор. Это правило нуля (rule of zero): класс, поля которого сами управляют ресурсами, не объявляет ни деструктора, ни своих методов копирования.
const-методы
Объект, объявленный с const, менять нельзя. Можно ли вызвать метод у такого объекта, компилятор решает по объявлению метода, а не по его телу. Поэтому у константного объекта, как и через ссылку (reference) const T&, можно вызвать только const-методы (const member functions). Это методы со словом const после списка параметров:
class Temperature {
public:
Temperature(int celsius) : celsius_(celsius) {}
int celsius() const { return celsius_; }
void warm() { ++celsius_; }
private:
int celsius_;
};Для const Temperature t(20); вызов t.celsius() компилируется, а t.warm() — нет. Внутри const-метода объект тоже константный: присвоить значение полю там нельзя, если поле не объявлено со словом mutable. Поэтому метод, который только читает поля, объявляют const: его можно вызвать для любого объекта, и константного, и обычного.
Частая ошибка
Метод только читает поле, но объявлен без const:
class Temperature {
public:
Temperature(int celsius) : celsius_(celsius) {}
int celsius() { return celsius_; }
private:
int celsius_;
};
int twice(const Temperature& t) {
return t.celsius() * 2; // ✗
}error: 'this' argument to member function 'celsius' has type 'const Temperature', but function is not marked constПараметр t — константная ссылка, а celsius() не обещает оставить объект неизменным, поэтому вызов не компилируется. Добавьте const после списка параметров: int celsius() const.