§ 13. Объекты

Наследование, инкапсуляция и полиморфизм

01 C++ — это язык, который сочетает в себе сразу несколько подходов к программирования, и основным подходом считается объектно-ориентированный. Этот подход описывает предметную область вашей задачи, используя объекты и их методы. Объект — это абстрактное понятие, которое включает в себя объекты и процессы (физические, математические и другие), а метод — это абстрактный функционал, связанный с объектом.

02 Допустим, вы решили написать программу, которая моделирует качку судна. В этой программе у вас непременно будет объект судно, параметрами которого будут углы поворота относительно каждой из осей координат, масса, геометрия и др., а одним из методов этого объекта будет повернуть судно на определенный угол вокруг заданной оси, в котором угол и ось будут параметрами. Это называет инкапсуляцией: объект судно скрывает в себе параметры, которые изменяются посредством вызова методов этого объекта и никак иначе.

03 Затем, вы решили написать программу, которая рисует на экране простые геометрические фигуры: треугольник, окружность, прямоугольник и т.п. В этой программе у вас непременно будут объекты, которые соответствуют каждой фигуре: объект треугольник с координатами каждой точки в качестве параметров, объект окружность с координатой центра и радиусом и объект прямоугольник с координатой левого верхнего и правого нижнего угла. У каждого из этих объектов будет метод нарисовать. Этот метод вызывается одинаково для каждого объекта, но реализация этого метода уникальна для каждого объекта. Это называется полиморфизмом — объекты разные, а функционал один и тот же и предоставляется через один и тот же интерфейс.

04 Наконец, вы решили написать программу, которая моделирует движение трехмерных объектов (компьютерную игру, например). Тогда в вашей программе обязательно появится объект, который является базовым для всех объектов, отображаемых на экране. Этот объект будет содержать координаты и какие-нибудь другие поля в качестве параметров. Все остальные объекты буду создаваться на его основе, наследуя все его параметры и методы. Это так и называется — наследование.

05 Инкапсуляция, наследование и полиморфизм являются основой объектно-ориентированного подхода к разработке программ. Такие программы состоят из объектов и методов, внутри которых вызываются методы других объектов, в которые еще одни объекты передаются в качестве параметров. Преимуществами данного подхода являются

Классы

06 В С++ для создания объекта сначала надо определить его класс. Это делается с помощью ключевого слова class. Класс является описанием общедоступного интерфейса объекта и содержит в себе список полей, список методов и список типов. Для каждого элемента класса определяется уровень доступа: private — элемент доступен только внутри класса, protected — элемент доступен внутри класса и во всех классах-наследниках, public — элемент доступен из любого контекста. Как правило, все поля класса делают доступными только внутри класса, а для доступа к ним используют общедоступные методы. Это выгодно, если надо измененить реализацию класса: поля можно поменять без изменения методов. Скрытые методы и типы используют для сокрытия внутренней реализации класса. Уровни доступа работают только на этапе компиляции, во время запуска программы они ни на что не влияют.

// файл ship.hh
/* asdasd */
class Ship {
private:
  float angleX = 0;
  float angleY = 0;
  float angleZ = 0;
  float mass = 0;
public:
  Ship(float mass); // конструктор
  ~Ship(); // деструктор
  void rotate(float delta, int axis); // метод (объявление)
  inline float getMass() const { return mass; } // метод (объявление и определение)
};
Объявление класса Ship с полями, методами, конструктором и деструктором. Файл ship.hh.

07 Для создания объекта используется тот же синтаксис, что и для объявления и определения переменных, но с указанием класса в качестве типа переменной. Для объектов объявление и определения совмещено, и при объявлении сразу вызывается конструктор соответствующего класса, а когда программа выходит из блока кода, в котором был объявлен объект, то вызывается деструктор класса. Конструктор и деструктор являются методами класса, вызов которых происходит автоматически. Привязка вызова этих методов к синтаксической структуре программы является главным отличием C++ от других языков.

int main() {
  Ship ship(123); // вызов конструктора Ship
  std::cout << "Ship mass: " << ship.getMass() << '\n';
  return 0;
  // вызов деструктора Ship
}
Автоматический вызов конструктора и деструктора класса Ship.

08 Конструкторы используются для инициализации полей объекта. Это можно сделать, передав в конструктор аргументы, и, написав в теле конструктора код, вычисляющий значения полей. Для полей, которые не были проинициализированы, вызывается конструктор по умолчанию (конструктор без аргументов), а для примитивных типов конструктор не вызывается, и такие поля имеют произвольные значения.

09 Деструкторы используются для освобождения ресурсов, которые были выделены в конструкторе. Обычно, это оперативная память, но может быть и любой другой системный ресурс, например, файловый дескриптор. Для всех полей деструкторы вызываются автоматически.

10 Методы класса можно объявить — то есть указать имя, типы аргументов и тип возвращаемого значения, — а можно определить — то есть написать тело метода. Для простых методов объявление и определение обычно совмещают, то есть сразу после объявления пишут тело метода. Это выгодно с точки зрения производительности, поскольку тела таких методов вставляются в место вызова оптимизатором (при условии, что они достаточно небольшие), и экономятся инструкции необходимые для вызова метода. Для сложных методов объявление и определения пишутся отдельно: объявление пишут в заголовочном файле, а определение — в основном файле. Сами файлы называют по имени класса. Если вы хотите, чтобы совместное объявление и определение работало корректно, то необходимо добавить ключевое слово inline к нему. Если метод не изменяет поля объекта, то добавляют ключевое слово const к его определению.

// файл ship.cc
#include "ship.hh"
Ship::Ship(float m): mass(m) {
  // конструктор
}
Ship::~Ship() {
  // деструктор
}
void Ship::rotate(float delta, int axis) {
  // метод
}
Определение класса Ship с полями, методами, конструктором и деструктором. Файл ship.cc.

Задания

Point1 балл

11 Создайте класс Point, который представляет собой точку в трехмерном пространстве. Напишите программу, в которой создаются объекты этого класса и выводятся на экран их координаты. Нужна ли инкапсуляция для этого класса?

Rectangle1 балл

12 Создайте класс Rectangle, который представляет собой прямоугольник в двухмерном пространстве. Напишите программу, в которой создаются объекты этого класса и выводятся на экран их параметры. Нужна ли инкапсуляция для этого класса? Какие наборы полей можно использовать для описания прямоугольника?

Triangle1 балл

13 Создайте класс Triangle, который представляет собой треугольник в трехмерном пространстве. В классе должно быть не больше трех полей. Напишите программу, в которой создаются объекты этого класса и выводятся на экран их параметры.

Ship2 балла

14 Создайте класс Ship, который представляет собой судно в трехмерном пространстве. Параметрами судна являются масса и углы поворота по каждой из осей. Методом класса является метод rotate, который поворачивает судно на указанный угол вокруг указанной оси. Напишите объявление класса в файле ship.hh, определение методов — в файле ship.cc, а функцию main — в файле main.cc. Как можно изменить поля, так чтобы упростить реализацию метода rotate?

Видео

Запись лекции 27.11.2021.