Глиф Учебники

Урок 9. Структуры и классы

Курс «C++» · урок 9 из 10 · 3 минуты чтения

Ученик — это имя и балл, точка — две координаты, товар — название, цена и количество. Хранить такие данные в отдельных массивах неудобно: легко перепутать, где чьё. Структура собирает связанные значения в один тип.

struct

C++
#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;
}
Вывод
5
struct Point double x; double y; новый тип: поля x и y Point a = {0, 0}; a.x 0 a.y 0 у каждой — свои поля Point b; b.x 3 b.y 4 b.x = 3; b.y = 4; a b 3 4 5 dist(a, b) = 5
struct Point описывает новый тип с полями x и y; у переменных a и b поля свои, к ним обращаются через точку, а dist(a, b) для точек (0, 0) и (3, 4) даёт 5.

Полям можно задать значения по умолчанию: int count = 0; внутри структуры — и у каждой новой переменной этого типа поле начнётся с нуля.

Вектор структур

Самое частое применение на олимпиадах — список записей:

C++
#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 78
Вывод
Boris 92

students[i].name — поле name у элемента i. Как отсортировать такой вектор по баллам, рассказывает урок STL: sort, map и set.

vector<Student> students(n); struct Student: name и score students[0] name "Anna" score 87 students[1] name "Boris" score 92 students[2] name "Vera" score 78 students[1].name поле name у элемента 1 наибольший score Boris 92
vector<Student> хранит записи подряд, и в каждой есть поля name и score; программа сравнивает поля score и выводит запись с наибольшим баллом — Boris 92.

Методы

В структуру можно положить и функции — они называются методами и работают с полями своей переменной:

C++
#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.

Зачем закрывать? Чтобы данные нельзя было испортить. Банковский счёт не должен уходить в минус, поэтому баланс меняется только через методы, которые это проверяют:

C++
#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. Это и есть инкапсуляция — класс сам отвечает за правильность своих данных.

main — снаружи класса acc.deposit(50); acc.getBalance() acc.balance = 1000000; не соберётся: поле private class Account public: открыто всем deposit withdraw getBalance Account конструктор private: только для методов класса string owner int balance
Снаружи класса Account доступны только методы из части public — deposit, withdraw, getBalance и конструктор, а поля owner и balance закрыты: acc.balance = 1000000 не соберётся.

Переменная типа класса называется объектом: acc — объект класса Account.

Конструктор

Конструктор — особый метод, который вызывается при создании объекта. Он называется так же, как класс, и не имеет типа результата. В примере выше конструктор принимает имя и начальный баланс:

C++
Account(const string& ownerName, int startBalance)
    : owner(ownerName), balance(startBalance) {
}

После двоеточия — список инициализации: поле owner получает ownerName, поле balance — startBalance. Это лучший способ задать полям начальные значения. Тело конструктора здесь пустое, но в нём можно делать и другие действия.

Если у класса есть конструктор с параметрами, создать объект без них (Account acc;) уже нельзя — компилятор потребует аргументы. Это удобно: объект не может появиться без нужных данных.

Указатель на структуру: ->

Если есть указатель на структуру, к полю обращаются через стрелку: p->x — то же, что (*p).x.

C++
#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 7

struct или class

Технически разница одна: в struct всё открыто по умолчанию, в class — закрыто. По привычке struct берут для простых наборов данных (точка, запись об ученике), а class — для объектов со своими правилами, где данные спрятаны за методами. Классы — основа объектно-ориентированного программирования (ООП); у него есть ещё наследование и виртуальные функции, это следующий шаг после основ.

Коротко

Проверьте себя

1. Чем class отличается от struct в C++?
2. Что пропущено в записи struct Point { int x; int y; }?
3. Как называется метод, который вызывается при создании объекта?
4. Как обратиться к полю x через указатель p на структуру?