Классы
Тип, который сам следит за своей корректностью
После урока ты сможешь
- Отличать класс с инвариантом от простой структуры
- Писать конструкторы со списком инициализации
- Делать методы const, если они ничего не меняют
Шаг 1 из 6
Структура, которая не может себя защитить
Опишем банковский счёт структурой:
struct Account {
std::string owner;
long long balance = 0;
};
Account acc{"Аня", 1000};
acc.balance = -50000; // ничто не мешает
У счёта есть правило: баланс не бывает отрицательным. Такое правило, которое должно выполняться всегда, называется инвариантом. В структуре его некому охранять: любой код в любом месте программы может записать в поле что угодно. В проекте на сто тысяч строк найти, кто испортил баланс, почти невозможно.
Класс решает эту проблему: данные закрыты, а менять их можно только через методы, которые проверяют правила.
#include <print>
#include <string>
#include <utility>
class Account {
public:
explicit Account(std::string owner) : owner_(std::move(owner)) {}
bool deposit(long long amount) {
if (amount <= 0) {
return false;
}
balance_ += amount;
return true;
}
bool withdraw(long long amount) {
if (amount <= 0 || amount > balance_) {
return false; // инвариант под защитой
}
balance_ -= amount;
return true;
}
long long balance() const { return balance_; }
const std::string& owner() const { return owner_; }
private:
std::string owner_;
long long balance_ = 0;
};
int main() {
Account acc("Аня");
acc.deposit(1000);
std::println("снять 5000: {}", acc.withdraw(5000));
std::println("снять 300: {}", acc.withdraw(300));
std::println("{}: {}", acc.owner(), acc.balance());
// acc.balance_ = -50000; // ошибка компиляции: поле private
}