6.1. Понятие классов в С++Идея ООП заключается в описании задачи на уровне объектов, которые в языке С++ называются классами. Например, класс может описывать объект линию, эллипс, прямоугольник. Но в отличие от структур, которые также могут комплексно описывать свойства каких-либо объектов, между классами возможны взаимодействия, которые выражаются тремя категориями: наследование, полиморфизм и инкапсуляция. Наследование – это механизм создания нового класса на основе ранее созданного. Наследование имеет смысл, если множество разнородных объектов имеют общие характеристики или функции. Так, в случае с графическими примитивами тип линии, цвет и толщина описываются одинаково на уровне языка программирования и логически отностятся к одной категории – свойства графического примитива. Поэтому эти элементы целесообразно выделить в отдельный класс – базовый и на основе него создавать новые классы – дочерние для более детального описания линии, эллипса и прямоугольника, используя механизм наследования. Полиморфизм – это процесс вызова и переопределение функций базового класса в дочерних. Полиморфизм позволяет общие функции дочерних классов выносить в базовый, а затем их вызывать из дочернего, полагая, что они определены в нем. Вместе с тем это не исключает возможности переопределения функций базового класса в дочерних. Инкапсуляция – это способ представления класса в виде «черного ящика». Это значит, что конечному пользователю класса (программисту) доступен лишь определенный набор функций и переменных для работы с классом. Часто ограничение доступа применяется для записи значений в переменные класса через функции, при запрещенном непосредственном доступе к переменным. Класс в языке С++ задается с помощью ключевого слова class, за которым следует его имя и в фигурных скобках {} дается его описание. После определения класса ставится точка с запятой. Ниже приведен пример описания класса для хранения координат графических примитивов:
Каждый класс имеет специальные функции, которые называются конструктор и деструктор. Конструктор класса вызывается всякий раз, когда объект создается в памяти ЭВМ и служит обычно для инициализации данных класса. Конструктор имеет то же имя, что и имя класса. Деструктор вызывается при удалении класса из памяти и используется, как правило, для освобождения ранее выделенной памяти под какие-либо данные этого класса. Имя деструктора совпадает с именем класса, но перед ним ставится символ ‘~’. Рассмотрим пример реализации конструктора и деструктора для класса CPos.
Здесь ключевое слово public используется для обеспечения общего доступа к функциям и переменным класса. Для создания нового экземпляра класса в памяти ЭВМ используется оператор new языка С++, а для удаления – оператор delete. Использование данных операторов для создания экземпляра класса CPos и его удаления выглядит следующим образом:
В результате выполнения этих двух строк программы на экране появятся сообщения:
Экземпляр класса также можно создать подобно обычным переменным без использования указателей, как показано ниже
В этом случае переменная pos называется представителем класса, у которого также вызывается конструктор при его создании и деструктор при его удалении из памяти. Следует отметить, что при создании нового экземпляра класса можно выполнять инициализацию различных переменных путем передачи их значений через конструктор. В этом случае конструктор должен быть объявлен с набором необходимых аргументов, например, так:
и процесс создания экземпляра класса принимает вид:
или
Такой способ описания и вызова конструктора представляет дополнительное удобство инициализации данных при создании нового объекта. При этом конструктор, как и любую функцию, можно перегружать. Это значит, что можно задать несколько типов конструкторов (с разным набором входных параметров) в одном и том же классе. Например, если создается экземпляр класса графического примитива, но для него неизвестны начальные и конечные координаты, то целесообразно вызвать конструктор CPos() без аргументов, а если координаты известны, то выполнить их инициализацию путем вызова конструктора с аргументами. Для описания нескольких типов конструкторов в одном классе достаточно дать их определения в нем:
В классах помимо переменных, конструкторов и деструкторов можно задавать описания и обычных функций, которые, в этом случае, называются методами. Например, в классе CPos для задания значений координат примитива целесообразно добавить функцию для ввода значений в переменные sp_x, sp_y, ep_x и ep_y. Это позволит, во-первых, не запоминать программисту имена этих переменных, а оперировать только одной функцией и, во-вторых, в самой функции можно реализовать необходимые проверки на истинность переданных значений координат перед их присваиванием переменным. Такую функцию можно описать в классе следующим образом:
В приведенном примере реализована функция SetParam(), которая перед присваиванием значений переменных выполняет проверку на их истинность. Здесь некоторое неудобство представляет то, что данная функция полностью описана в классе CPos, а описание большого числа функций в одном классе делает текст программы трудночитаемым. Поэтому обычно в классах записывают лишь прототипы функций, а их реализации приводят отдельно после описания класса. Для того чтобы описать реализацию функции SetParam() вне класса CPos перед именем функции ставится имя класса с оператором глобального разрешения ‘::’ как показано ниже:
а перед ней должно идти следующее определение класса:
Аналогичным образом можно давать описание конструкторов и деструкторов за пределами класса. Учитывая, что данные функции ничего не возвращают вызывающей программе и не имеют типов, то их внешняя реализация будет иметь вид:
Функцию SetParam() можно вызывать через указатель на класс, используя оператор ‘->’ или через представитель с помощью оператора ‘.’:
Таким же образом можно обращаться и к переменным класса:
Здесь можно заметить, что значения переменных sp_x, sp_y, ep_x и ep_y могут быть заданы как непосредственно при обращении к ним, так и с помощью функции SetParam(). В результате проверка, реализованная в данной функции, может быть проигнорирована программистом. Часто такая ситуация недопустима, например, при использовании готовых классов библиотек MFC, VCL, OWL и др. В связи с этим в классах для переменных и функций предусмотрена возможность установки разных уровней доступа, которые определяются тремя ключевыми словами: public, private и protected. Ключевое слово public означает общий доступ к переменным и функциям класса. Уровень доступа private указывает на частный способ доступа к элементам класса и устанавливается по умолчанию при описании класса. Частный уровень доступа дает возможность обращаться к переменным и функциям только внутри класса и запрещает извне, например, через представители или указатели на класс. Режим доступа protected также как и private запрещает доступ к элементам класса через представители и указатели, но разрешает обращаться к ним из дочерних классов при наследовании. Учитывая эти три режима доступа, класс для работы с координатами графических объектов целесообразно записать в таком виде:
Здесь раздел private ограничивает доступ пользователю класса к переменным sp_x, sp_y, ep_x и ep_y только функцией SetParam(). Следует также отметить, что отсутствие раздела public вначале описания класса привело бы к тому, что все функции класса CPos имели бы область видимости private. В результате доступ к конструктору и деструктору был бы запрещен, и создание нового объекта стало бы невозможным. Аналогичная картина имеет место и в режиме доступа protected, но в отличие от private класс можно использовать как базовый в механизме наследования. Это свойство полезно использовать для запрета создания экземпляров класса, что бывает необходимым, если он является лишь промежуточным звеном в иерархии объектов и не представляет ценности как отдельный объект.
Видео по теме
С++ с нуля: урок 1 - переменные, оператор присваивания С++ с нуля: урок 2 - арифметические операции С++ с нуля: урок 3 - директивы препроцессора С++ с нуля, урок 4: условные операторы if и switch С++ с нуля: урок 5 - операторы циклов while, for и do while С++ с нуля: урок 6 - массивы, метод всплывающего пузырька С++ с нуля: урок 7 - строки и функции работы с ними С++ с нуля: урок 8 - функции: прототипы, перегрузка, рекурсия С++ с нуля: урок 9 - области видимости переменных С++ с нуля: урок 10 - битовые операции И, ИЛИ, НЕ, XOR С++ с нуля: урок 11 - структуры С++ с нуля: урок 12 - объединения, перечисления, typedef С++ с нуля: урок 13 - указатели и ссылки, выделение памяти С++ с нуля: урок 14 (часть 1) - функции работы с файлами С++ с нуля: урок 14 (часть 2) - функции работы с файлами С++ с нуля: урок 15 - стек, теория и практика С++ с нуля: урок 16 - связные списки, теория и практика С++ с нуля: урок 17 - бинарное дерево, теория и практика
|