Урок 9. Структуры и классы
Ученик — это имя и балл, точка — две координаты, товар — название, цена и количество. Хранить такие данные в отдельных массивах неудобно: легко перепутать, где чьё. Структура собирает связанные значения в один тип.
struct
#include <iostream>
#include <cmath>
using namespace std;
struct Point {
double x;
double y;
};
double dist(Point a, Point b) {
double dx = a.x - b.x;
double dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
int main() {
Point a = {0, 0};
Point b;
b.x = 3;
b.y = 4;
cout << dist(a, b) << "\n";
return 0;
}5struct Point { ... };создаёт новый типPoint. После закрывающей скобки — точка с запятой, её часто забывают.- Переменные внутри структуры — поля:
xиy. Point a = {0, 0};— значения полей по порядку. Поля по отдельности — через точку:b.x = 3.- Структуры можно передавать в функции и возвращать из них, как обычные значения.
Полям можно задать значения по умолчанию: int count = 0; внутри структуры — и у каждой новой переменной этого типа поле начнётся с нуля.
Вектор структур
Самое частое применение на олимпиадах — список записей:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct Student {
string name;
int score;
};
int main() {
int n;
cin >> n;
vector<Student> students(n);
for (int i = 0; i < n; i++) {
cin >> students[i].name >> students[i].score;
}
Student best = students[0];
for (const Student& s : students) {
if (s.score > best.score) {
best = s;
}
}
cout << best.name << " " << best.score << "\n";
return 0;
}3
Anna 87
Boris 92
Vera 78Boris 92students[i].name — поле name у элемента i. Как отсортировать такой вектор по баллам, рассказывает урок STL: sort, map и set.
Методы
В структуру можно положить и функции — они называются методами и работают с полями своей переменной:
#include <iostream>
using namespace std;
struct Rectangle {
int width;
int height;
int area() const {
return width * height;
}
int perimeter() const {
return 2 * (width + height);
}
};
int main() {
Rectangle r = {3, 5};
cout << r.area() << " " << r.perimeter() << "\n";
return 0;
}15 16Внутри метода поля пишутся просто по имени: width, а не r.width. Слово const после скобок обещает, что метод не меняет поля. Ставьте его у всех методов, которые только читают данные: такие методы можно вызывать и у const-переменных, и через const-ссылки.
Классы: public и private
Класс (class) устроен так же, как структура, но с одним отличием: по умолчанию его поля и методы закрыты (private) — к ним нельзя обратиться снаружи, только из методов самого класса. Открытая часть отмечается словом public.
Зачем закрывать? Чтобы данные нельзя было испортить. Банковский счёт не должен уходить в минус, поэтому баланс меняется только через методы, которые это проверяют:
#include <iostream>
#include <string>
using namespace std;
class Account {
public:
Account(const string& ownerName, int startBalance)
: owner(ownerName), balance(startBalance) {
}
void deposit(int amount) {
if (amount > 0) {
balance += amount;
}
}
bool withdraw(int amount) {
if (amount <= 0 || amount > balance) {
return false;
}
balance -= amount;
return true;
}
int getBalance() const {
return balance;
}
private:
string owner;
int balance;
};
int main() {
Account acc("Anna", 100);
acc.deposit(50);
cout << acc.getBalance() << "\n";
cout << acc.withdraw(500) << "\n";
cout << acc.withdraw(30) << "\n";
cout << acc.getBalance() << "\n";
return 0;
}150
0
1
120Запись acc.balance = 1000000; в main не соберётся: компилятор сообщит, что поле private. Это и есть инкапсуляция — класс сам отвечает за правильность своих данных.
Переменная типа класса называется объектом: acc — объект класса Account.
Конструктор
Конструктор — особый метод, который вызывается при создании объекта. Он называется так же, как класс, и не имеет типа результата. В примере выше конструктор принимает имя и начальный баланс:
Account(const string& ownerName, int startBalance)
: owner(ownerName), balance(startBalance) {
}После двоеточия — список инициализации: поле owner получает ownerName, поле balance — startBalance. Это лучший способ задать полям начальные значения. Тело конструктора здесь пустое, но в нём можно делать и другие действия.
Если у класса есть конструктор с параметрами, создать объект без них (Account acc;) уже нельзя — компилятор потребует аргументы. Это удобно: объект не может появиться без нужных данных.
Указатель на структуру: ->
Если есть указатель на структуру, к полю обращаются через стрелку: p->x — то же, что (*p).x.
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
};
int main() {
Point a = {2, 7};
Point* p = &a;
p->x = 10;
cout << a.x << " " << p->y << "\n";
return 0;
}10 7struct или class
Технически разница одна: в struct всё открыто по умолчанию, в class — закрыто. По привычке struct берут для простых наборов данных (точка, запись об ученике), а class — для объектов со своими правилами, где данные спрятаны за методами. Классы — основа объектно-ориентированного программирования (ООП); у него есть ещё наследование и виртуальные функции, это следующий шаг после основ.
Коротко
struct Имя { поля; };— свой тип; после}нужна;.- Поле — через точку
a.x, через указатель — стрелкойp->x. - Методы — функции внутри структуры или класса;
constпосле скобок — метод не меняет поля. class— какstruct, но закрыт по умолчанию; открытое — послеpublic:, закрытое — послеprivate:.- Конструктор создаёт объект; поля задаются в списке инициализации после
:.