Конструкторы и список инициализации
Закрытое поле нельзя задать снаружи: строка t.celsius_ = 20; для объекта с закрытым полем не скомпилируется. Значение нужно передать в момент создания объекта, и для этого в классе объявляют конструктор:
#include <iostream>
class Temperature {
public:
Temperature(int celsius) : celsius_(celsius) {}
int fahrenheit() { return celsius_ * 9 / 5 + 32; }
private:
int celsius_;
};
int main() {
Temperature t(20);
std::cout << t.fahrenheit() << '\n'; // 68
}Конструктор
Конструктор — метод с именем класса и без типа результата. Он выполняется сам, когда объект создаётся: строка Temperature t(20); вызывает конструктор с аргументом 20. Аргументы можно передать и в фигурных скобках, как в Temperature t{20};.
Конструктор по умолчанию
Конструктор без параметров называют конструктором по умолчанию, его вызывает объявление Temperature t;. Если в классе нет ни одного конструктора, компилятор создаёт конструктор по умолчанию сам. Но стоит объявить конструктор с параметрами, и строка Temperature t; перестанет компилироваться, пока конструктор без параметров не объявят явно.
Слово explicit
Компилируется и запись Temperature t = 20;. Конструктор с одним параметром неявно преобразует число 20 в объект Temperature. Слово explicit перед конструктором запрещает такое преобразование: тогда объект создают только записями t(20) и t{20}.
Список инициализации
Запись : celsius_(celsius) между заголовком конструктора и его телом — список инициализации. Он задаёт полям начальные значения до того, как начнёт выполняться тело конструктора, поэтому тело здесь пустое. Если поле — объект класса, в скобках передают аргументы его конструктора. Поле с const в теле конструктора уже не изменить, поэтому его задают именно здесь или в объявлении.
Поле может получить значение и в своём объявлении, как count_ ниже. Это значение используется, когда конструктор не упомянул поле в списке, а запись в списке его заменяет:
class Counter {
public:
Counter() {}
Counter(int start) : count_(start) {}
int value() { return count_; }
private:
int count_ = 0;
};Для Counter a; выполняется пустой конструктор по умолчанию, и a.value() возвращает 0. Для Counter b(5); список задаёт count_ значение 5, и = 0 не используется.
Порядок инициализации задаёт класс, а не список: поля инициализируются в порядке объявления, даже если в списке они записаны иначе. Компилятор предупреждает о таком расхождении, поэтому список пишут в том же порядке, что и поля.
Частая ошибка
Одно поле инициализируют через другое, объявленное ниже:
class Segment {
public:
Segment(int start, int length)
: start_(start), end_(start_ + length) {} // ✗
int end() { return end_; }
private:
int end_;
int start_;
};warning: field 'start_' will be initialized after field 'end_' [-Wreorder-ctor]Поле end_ объявлено первым и инициализируется первым, а start_ в этот момент ещё не получило значения. Чтение start_ здесь — неопределённое поведение, хотя компилятор только предупреждает. Объявите start_ выше end_ или вычисляйте end_ из параметров: end_(start + length).
Главное
- Конструктор выполняется сам при создании объекта и задаёт начальные значения закрытым полям.
- После объявления конструктора с параметрами конструктор по умолчанию сам уже не появляется.
- Список инициализации задаёт поля до начала тела конструктора.
- Поля инициализируются в порядке объявления в классе, а не в порядке списка.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.