Українська
const, static та агрегати
Константність, this та спостереження за станом
Метод із const після списку параметрів обіцяє не змінювати звичайні поля об’єкта через this. Його можна викликати для константного об’єкта та через константне посилання. Для методу balance() const результат є спостереженням, а withdraw() змінює стан і не має такого кваліфікатора.
this – вказівник на об’єкт, для якого викликано нестатичний метод. У const-методі доступ через нього є константним. Явний запис this-> зазвичай не потрібен, але може розрізняти поле та однойменний параметр. Повернення *this за посиланням дозволяє ланцюжки операцій, однак не слід робити інтерфейс складним лише заради короткого запису.
mutable дозволяє змінювати окреме поле навіть у const-методі. Типовий приклад – кеш похідного значення, який не змінює логічний зміст об’єкта. Це не дозвіл приховувати зміну балансу або оцінки студента. Сам по собі mutable також не робить доступ із кількох потоків безпечним.
Метод доступу не повинен без потреби повертати неконстантне посилання на закритий контейнер: користувач тоді отримає змогу обійти всі перевірки. Для коротких чисел достатньо повернення за значенням. Для складних даних розглядають константне посилання, копію або спеціальну операцію читання з чітко описаним часом життя результату.
Статичні члени та вкладені типи
Статичний метод не має this: його викликають для класу, а не для конкретного стану. Він підходить для перевірки формату номера автомобіля або фабрики, якщо операція справді належить поняттю класу. Для звичайної незалежної арифметики вільна функція часто простіша за клас-обгортку.
static constexpr зручно представляє незмінну межу: максимальну температуру чи місткість стандартної моделі. Зміна inline static впливає на всі екземпляри. Треба розрізняти лічильник колись створених об’єктів, лічильник живих об’єктів і наступний ідентифікатор: це різні контракти, особливо коли з’являються копії та переміщення.
Вкладений enum class Mode групує допустимі режими біля типу, якому вони належать. Назва Thermostat::Mode::heat чіткіша за число 1, а неявного перетворення переліку на ціле число немає. Стан режиму не слід дублювати кількома булевими полями, які можуть суперечити одне одному, наприклад одночасно «вимкнено» та «нагрівання».
Вкладений тип не створює автоматично вкладений об’єкт. Оголошення переліку визначає допустимі значення, а конкретне поле mode_ зберігає поточний режим. Це така сама різниця між типом і екземпляром, як між класом рахунку та двома конкретними рахунками.
Агрегати та призначена ініціалізація
Агрегат (aggregate) зручний для відкритого набору даних, якщо його поля не утворюють складного прихованого інваріанта. Приклад оголошення: struct Point { int x = 0; int y = 0; };. Об’єкт можна створити як Point p{.x = 1, .y = 2};. Це призначена ініціалізація C++20, яка явно називає поля та полегшує читання.
Порядок призначених полів у C++ має відповідати порядку їхнього оголошення. Не слід переносити довільні правила з мови C: можливості подібного синтаксису не повністю однакові. Пропущені поля отримують свій ініціалізатор або ініціалізуються за відповідними правилами агрегату; це варто перевіряти для конкретного типу.
Клас із користувацьким конструктором, який перевіряє аргументи, не можна автоматично вважати агрегатом. Для BankAccount важливіше не пропустити перевірку початкового балансу, ніж отримати короткий синтаксис заповнення полів. Відкриті структури та інкапсульовані класи доповнюють одне одного, а не утворюють рейтинг «кращих» типів.