Навчальний простір / C++
Об’єктно-орієнтоване програмування

Класи та інкапсуляція: інваріанти й операції

Клас поєднує стан і операції навколо інваріанта.

Зміст матеріалу

Що ви опануєте

Ви навчитеся проєктувати клас навколо правил предметної області, створювати коректний початковий стан і підтримувати його через публічні операції. Приклад — навчальне завдання з назвою та балом. Клас має гарантувати непорожню назву й бал у межах 0–100 незалежно від того, хто його використовує.

Клас, екземпляр і члени

Клас описує користувацький тип. Екземпляр — конкретний об’єкт цього типу. Поля зберігають стан, методи визначають операції. public відкриває доступ користувачу класу, private обмежує його реалізацією. У class доступ за замовчуванням private, у struct — public. Обидва можуть мати методи, конструктори та інші можливості.

Інкапсуляція організує доступ до стану через контракт типу. Приховане поле саме по собі не гарантує коректності: публічний метод може записати некоректне значення. Тому спочатку визначають інваріант, який має виконуватися для кожного доступного користувачу коректного стану. Для оцінки це діапазон балу та непорожня назва.

Конструктор і початковий стан

Конструктор має ім’я класу й не має типу результату. Список ініціалізації створює поля до виконання тіла. Якщо коректний об’єкт створити неможливо, конструктор може кинути виняток. Користувач тоді не отримує частково готовий об’єкт. Порядок ініціалізації полів визначається порядком їх оголошення у класі.

assignment.cpp cpp
#include <iostream>
#include <stdexcept>
#include <string>
#include <utility>

class Assignment {
    std::string title_;
    int score_;
    static int checked_score(int value) {
        if (value < 0 || value > 100) throw std::invalid_argument("score");
        return value;
    }
public:
    Assignment(std::string title, int score)
        : title_(std::move(title)), score_(checked_score(score)) {
        if (title_.empty()) throw std::invalid_argument("title");
    }
    const std::string& title() const { return title_; }
    int score() const { return score_; }
    void update_score(int value) { score_ = checked_score(value); }
};

int main() {
    Assignment work{"Моделювання", 70};
    work.update_score(85);
    std::cout << work.title() << ": " << work.score() << '\n';
}

checked_score є статичною функцією: їй не потрібний конкретний екземпляр. Вона перевіряє значення до присвоєння, тому невдала зміна не псує попередній бал. std::move дозволяє передати зберігання параметра в поле. const після методу означає, що через цей виклик не можна змінювати звичайні поля об’єкта.

Контракт читання

Метод score() повертає маленьке значення. title() повертає const std::string&, щоб позичити читання без копії. Це посилання залежить від часу життя об’єкта. Якщо зберегти його після знищення Assignment, доступ стане некоректним. Коли потрібний незалежний результат, можна скопіювати рядок. Тип результату має виражати очікувану залежність.

Геттери не зобов’язані існувати для кожного поля. Публічний інтерфейс описує корисні запити й дії предметної області. Метод passed() може повертати результат правила порогу, не змушуючи кожного користувача повторювати його. Сеттер, який приймає будь-яке число без перевірки, руйнує користь інваріанта.

Композиція

Композиція означає, що об’єкт містить інші об’єкти як частини стану. Assignment містить std::string, який сам керує пам’яттю символів. Його деструктор викликається автоматично при знищенні зовнішнього об’єкта. Це дозволяє застосувати правило нуля: класу часто достатньо стандартних ресурсних полів без ручного деструктора.

Якщо група містить std::vector<Assignment>, вона володіє своїми елементами. Публічні операції групи повинні підтримувати власні правила, наприклад унікальні ідентифікатори. Посилання на елемент vector може інвалідуватися при зростанні. Композиція не скасовує правила часу життя й контейнерів.

Винятки та гарантії

Перед зміною поля перевірте нове значення. У прикладі checked_score може кинути виняток до присвоєння. Це забезпечує збереження старого балу для цієї операції. Для складніших змін кількох полів потрібно обґрунтувати, що стан залишиться узгодженим при помилці посеред процесу. Тимчасове обчислення нового стану може спростити таку гарантію.

Конструктор не повинен приховувати важливі помилки «виправленням» без контракту. Наприклад, автоматичне обрізання 150 до 100 може замаскувати неправильний розрахунок викликача. Якщо така нормалізація є правилом, назвіть її явно й перевірте прикладами. Для оцінки в цій лекції погоджено відхилення некоректного значення.

Типові помилки й професійна практика

Типові помилки: незадокументований допустимий діапазон, публічні змінювані поля, метод без const для читання й повернення змінюваного посилання на внутрішній стан. Надмірний універсальний клас із багатьма незалежними правилами важко перевіряти. Починайте з однієї чіткої відповідальності та мінімального інтерфейсу.

Практичне осмислення

Додайте правило непорожнього нового заголовка та операцію перейменування. Визначте, чи дозволені лише пробіли. Напишіть перевірки, які підтверджують збереження старої назви при відмові. Поясніть, чому звичайний private не забезпечує цього без контракту методів. Назвіть ресурсні поля, які дозволяють не писати власний деструктор.

Інкапсуляція та узгоджені переходи стану

Клас має відповідальність за інваріант від успішного створення до знищення. Конструктор Assignment перевіряє початкову назву й бал; метод зміни бала повинен зберігати той самий допустимий діапазон. Якщо перевірка існує лише в конструкторі, пізніший відкритий setter може створити стан, який інші методи помилково вважають коректним.

Порядок дій у методі важливий для винятків. Спочатку перевірте нове значення, потім змініть поле. Якщо перевірка відхиляє вхід, об’єкт зберігає попередній стан. Для кількох взаємопов’язаних полів потрібно готувати узгоджене оновлення, щоб виняток посеред операції не залишив половину нового стану.

Const-метод описує читання через цей об’єкт і дозволяє використовувати його через const-посилання. Це допомагає будувати API з явним розділенням спостереження та зміни. Getter, який повертає неконстантне посилання на внутрішню колекцію, може надати зовнішньому коду шлях обходу перевірок. Форма повернення повинна відповідати потрібній свободі клієнта.

Для навчального аналізу запишіть інваріант природною мовою та як умову. Далі перелічіть усі публічні операції, що можуть змінити стан. Для кожної задайте передумову, успішну постумову та стан після відмови. Якщо операція не має зрозумілої предметної мети, перегляньте її необхідність. Приватні поля корисні тоді, коли публічний інтерфейс дозволяє виконати потрібні дії зі збереженням правил.

Схема процесу

Класи та інкапсуляція: інваріанти й операціїОберіть елемент, щоб побачити пояснення

Визначаємо коректні стани.

Самоперевірка

Перевірте розуміння
Коли потрібно перевіряти нове значення поля?
Як обґрунтувати відповідь своїми словами?
Перевірка до присвоєння дозволяє відхилити некоректне значення без порушення поточного інваріанта.

Підсумок

Клас поєднує стан і операції навколо інваріанта. Конструктор встановлює його, методи зберігають, а типи параметрів і результатів пояснюють доступ та час життя. Композиція з ресурсними типами підтримує правило нуля.

Джерела

Опрацювали матеріал?

Збережіть цей крок у своєму прогресі.

← Повернутися до дисципліни
© 2026 Анастасія Іскандарова-МалаЕлектронний навчальний посібник · ДДТУ

Пошук у посібнику

Спробуйте «C++», «RAII» або «бази даних».