Продолжаем изучать классы

Класс полезен не только тем, что объединяет поля и методы. Главная польза — контроль состояния. Если поле закрыто, объект может сам проверять, какие значения допустимы.
Закрытые поля защищают состояние. Открытые методы задают разрешённые действия. Конструктор с параметрами помогает создать объект сразу в правильном состоянии.
Проверяющий метод
#include <iostream>
using namespace std;
class Sensor {
public:
bool set(int value) {
if (value < 0) {
return false;
}
if (value > 400) {
return false;
}
dist = value;
return true;
}
int get() {
return dist;
}
private:
int dist = 0;
};
int main() {
Sensor front;
if (front.set(250) == true) {
cout << front.get() << endl;
}
return 0;
}
Метод set не позволяет записать отрицательное расстояние или слишком большое значение.
Конструктор с параметром
#include <iostream>
using namespace std;
class Sensor {
public:
Sensor(int value) {
if (value < 0) {
value = 0;
}
if (value > 400) {
value = 400;
}
dist = value;
}
int get() {
return dist;
}
private:
int dist;
};
int main() {
Sensor front(150);
cout << front.get() << endl;
return 0;
}
Объект front создаётся сразу со значением 150.
Интерфейс и реализация
Интерфейс — это то, чем пользуется внешний код. Реализация — то, как класс устроен внутри.
| Часть | Пример | Кто должен знать |
|---|---|---|
| интерфейс | set, get | пользователь класса |
| реализация | поле dist, проверки внутри set | автор класса |
Если внешний код не трогает поле напрямую, внутреннее устройство класса можно изменить спокойнее.

Разделение на файлы
В большом проекте класс обычно делят на заголовочный файл и файл реализации.
Sensor.h
#ifndef SENSOR_H
#define SENSOR_H
class Sensor {
public:
Sensor(int value);
bool set(int value);
int get();
private:
int dist;
};
#endif
Sensor.cpp
#include "Sensor.h"
Sensor::Sensor(int value) {
if (value < 0) {
value = 0;
}
if (value > 400) {
value = 400;
}
dist = value;
}
bool Sensor::set(int value) {
if (value < 0) {
return false;
}
if (value > 400) {
return false;
}
dist = value;
return true;
}
int Sensor::get() {
return dist;
}
main.cpp
#include <iostream>
#include "Sensor.h"
using namespace std;
int main() {
Sensor front(100);
cout << front.get() << endl;
return 0;
}
Компиляция из консоли:
g++ main.cpp Sensor.cpp -o main
Защитные строки в .h
#ifndef SENSOR_H
#define SENSOR_H
...
#endif
Эти строки защищают заголовок от повторного подключения в одной сборке. Подробности препроцессора можно изучить позже; сейчас важно не удалять такую защиту.
Практика
- Создайте класс
Sensorс закрытым полемdist. - Добавьте метод
set, который принимает только значения от0до400. - Добавьте метод
get. - Добавьте конструктор с начальным значением.
- Разделите класс на
Sensor.h,Sensor.cppиmain.cpp.
Проверьте себя
- Почему поля лучше делать закрытыми?
- Что возвращает метод
set, если значение неверное? - Чем интерфейс отличается от реализации?
- Зачем класс делят на
.hи.cpp? - Для чего нужны защитные строки в заголовке?
Сначала ответьте без подсказки. Ответ можно считать полным, если вы:
- формулируете основную мысль своими словами;
- называете важные условия, ограничения или меры безопасности;
- для схемы, кода или расчёта показываете ход решения и ожидаемый результат.
Если один из пунктов объяснить не получается, найдите соответствующую главу статьи, перечитайте её и повторите ответ.
Словарь статьи
| Термин | Значение |
|---|---|
| интерфейс | доступные снаружи методы класса |
| реализация | внутренний код и поля класса |
| заголовочный файл | файл с объявлением класса |
| файл реализации | файл с кодом методов |
| защита заголовка | приём против повторного подключения |
| состояние объекта | значения его полей |
Связанные темы
- Классы — базовые понятия.
- Функции — объявления и определения.
- Наследование классов — общий интерфейс для похожих классов.
Источники
- C++ Working Draft. Classes: https://eel.is/c++draft/class
- Microsoft Learn. Header files: https://learn.microsoft.com/en-us/cpp/cpp/header-files-cpp