Базовые и производные классы
Стр 1 из 3Следующая ⇒ Глава 3. НАСЛЕДОВАНИЕ Основные понятия Одной из важнейших концепций объектно-ориентированного программирования является наследование. Наследование – это форма многократного использования программных средств, при котором классы создаются поглощением существующих данных и поведений класса с приданием им новых возможностей. При создании класса вместо написания абсолютно новых переменных и функций программист может обозначить факт того, новый класс должен наследовать переменные и функции другого класса. Определенный ранее класс называется базовым классом, а новый производным. После создания производный класс, в свою очередь, может стать базовым для другого класса. Прямой базовый класс – это базовый класс, из которого совершает явное наследование производный класс. Косвенный базовый класс наследуется из двух или более уровней выше. Если существует несколько уровней наследования, то говорят об иерархии классов. Производный класс, которому добавляется новая функциональность (а значит и новые члены), по размеру больше своего базового класса. Он более специфичен, чем базовый класс и представляет более специализированную группу объектов. Члены производного класса могут использовать открытые (public) и защищенные (protected) члены всех базовых классов. Члены производного класса могут иметь такие же имена, что и члены базовых классов. В этом случае для явного указания используется оператор разрешения области видимости :: (двойное двоеточие) с именем базового класса. В языке С++ допускается наследование от многих базовых классов. В этом случае говорят множественном наследовании. При множественном наследовании объект производного класса, содержит подобъекты каждого из базовых классов.
При проектировании системы классов возникает проблемы выбора формы взаимодействия между классами: включить класс в качестве члена (как это делалось в предыдущей главе) или сделать его базовым. Ключевым отношением между классами, которым следует руководствоваться при выборе наследования надо считать «является», а для включения – отношения «содержит» или «имеет». Например, «студент является индивидуумом» типичное отношение между базовым классом, описывающим индивидуума и производным классом, описывающим студента. Другой пример – «индивидуум имеет имя» подразумевает включения объекта класса «имя» в качества атрибута класса «индивидуум», как это было сделано в примерах предыдущей главы с объектом класса Name, который являлся членом класса Person.
Базовые и производные классы Рассмотрим производный класс с именем Employee ( от англ. employee – служащий), для которого в качестве базового выберем класс Person, разработанный в двух предшествующих главах. Прежде всего, подчеркнем, что справедливость утверждения «служащий является индивидуумом» дает нам основание считать этот проект правильным. Как и прежде, для описания класса Employee будем использовать два файла: Employee.h и Employee.cpp. На рис. 3.1 представлен пример простейшего класса Employee наследующего класс Person. Собственно класс Employee содержит только два члена: конструктор по умолчанию и деструктор. Причем обе эти функции-члены пустые (ничего не исполняют).
Рис 3.1. Пример простейшего класса Employee, наследующего класс Person.
Заголовок class Employee: public Person указывает на то, что класс Employee наследует класс Person или, по-другому класс Person является базовым для производного класса Employee. Перед именем базового класса Person в заголовке указан модификатор public, указывающий на то, что класс Person является открытым базовым классом. Члены производного класса могут использовать открытые (public) и защищенные (protected) члены базового класса. Более того, все открытые члены открытого базового класса становятся равноправными открытыми членами производного класса и могут применяться пользователем наравне с членами самого производного класса. Если класс Employee сам выступит в качестве базового класса, то для всех его членов, включая унаследованные, будет действовать то же правило, что в случае с классами Employee и Person. Такое наследование называется открытымнаследованием.
На рис. 3.2 приведен пример программы, в которой создается и используется объект класса Employee, а на рис. 3.3 результат ее работы. Для установки значений атрибутов объекта класса Employee применяются методы setFirstName, setLastName, setPatrName и setAddress принадлежащие базовому классу Person, но ставшие по правилу наследования полноправными открытыми методами объекта класса Employee. Как и прежде для обработки ошибок применяется механизм исключений. В try-блоке закомментирован код, вызывающий исключение типа Person:Exception (конечно, при условии, что инкапсулированы исключения, генерируемые членами классов Address и Person::Name, см. раздел 2.3). Если убрать комментарии с этих строк, то результат работы программы будет примерно такой, как на рис. 3.4. Рис 3.2. Пример программы применяющей объект класса Employee
\ Рис 3.3. Результат выполнения программы на рис. 3.2
Рис 3.4. Результат обработки ошибки в программе на рис. 3.2
Выполнение оператора объявления объекта s1 класса Employee в рассматриваемом примере автоматически влечет выполнение конструктора класса по умолчанию. При запуске конструктора производного класса обязательно должны быть выполнен конструктор базового класса. Конструктор базового класса может быть вызван в заголовке конструктора производного класса, также как это делалось для инициализации членов класса в примерах главы 2. В том случае, если конструктор базового класса не указан в заголовке конструктора, то компилятор сгенерирует вызов конструктора по умолчанию. При удалении производного класса, вызывается деструктор, который (это делается автоматически компилятором) выполняет деструкторы всех базовых классов, в обратном порядке запуска их конструкторов. На рис. 3.5 демонстрируются конструкторы, в заголовке которых указан вызов конструктора базового класса. Первом случае вызывается конструктор по умолчанию (его можно было бы не указывать), во втором – конструктор с аргументами, в т третьем копирующий конструктор.
Рис 3.5. Конструкторы, вызывающие конструкторы базовых классов Если в заголовке класса перед именем базового класса установлен модификатор protected ( защищенный ), то это приведет к тому, что все открытые (public) и защищенные (protected)члены базового класса будут доступны только членам производного класса и членам производного от производного класса, но не доступны для прямого вызова из программы, как это было в случае открытого наследования. Возвращаясь к примеру на рис. 3.1 и 3.2, отметим, что если бы там применялось защищенное наследование, то компиляция программы на рис. 3.1 завершилась бы с ошибками, т. к. здесь используются унаследованные, но защищенные методы класса Person. Обратите внимание на копирующий конструктор класса Epmloyee. В нем осуществляется вызов аналогичного конструктора класса Person, но в качестве параметра ему передается объект класса Epmloyee, при том, что в спецификации копирующего конструктора класса Person параметр должен иметь тип Person. Дело в том, что в языке C++ возможно специальное преобразование производного класса к базовому, но об этом будет подробно рассказано в следующей главе. В случае закрытого наследования (перед именем базового класса указывается модификатор private) действует такое же правило, что и при защищенном наследовании, но если такой производный класс выступит в качестве базового, то для нового производного класса не будут доступны член косвенного базового класса, даже если новое наследование было отрытым. По все видимости, защищенное и особенно закрытое наследование следует примять, когда производный класс стремится полностью инкапсулировать базовый класс. Обратите внимание, что ни для какого вида наследования членам производного класса не доступны закрытые члены базового класса. В противном случае сама концепция закрытых членов теряет смысл.
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|