Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лр-2(Классы).docx
Скачиваний:
4
Добавлен:
16.08.2019
Размер:
142.88 Кб
Скачать

Наследование

Наследование (inheritance) является одним из самых важных механизмов в ООП. Любой класс может наследоваться от другого класса, а это значит, что он будет иметь все те же члены, что и класс, от которого он унаследован. В терминологии ООП класс, от которого наследуется другой класс (или, другими словами, создается производный класс) называется родительским или базовым классом. Важно обратить внимание на то, что в С# напрямую наследоваться классы могут только от одного базового класса, хотя у базового класса может быть свой собственный базовый класс и т.д.

Механизм наследования позволяет расширять или создавать специфические классы от одного более общего базового класса. Например, возьмем класс, представляющий животное с фермы. Этот класс мог бы называться Animal (Животное) и обладать методами вроде EatFood () (Кормить) или Breed() (Разводить). От него можно было бы создать производный класс по имени Cow (Корова), который бы поддерживал все те же самые методы, но при этом также имел и свои собственные, например, Моо() (Мычать) и SupplyMilk() (Давать молоко), а также еще один производный класс по имени Chicken (Курица) с методами Cluck() (Кудахтать) и LayEgg() (Снести яйцо).

В UML наследование изображается с помощью стрелок, как показано на рис.

Рис. Представление отношений наследования в UML На рис. возвращаемые типы членов для простоты не показаны.

При применении наследования от базового класса важным становится вопрос о доступности членов. Приватные члены базового класса не являются доступными для производного класса, а общедоступные — являются. Однако общедоступные члены доступны как для производного класса, так и для внешнего кода. Поэтому если бы использовать можно было только два таких уровня доступности, создание члена, доступного как для базового, так и для производного класса, но не для внешнего кода, оказалось бы невозможным.

Для исключения вероятности возникновения подобной проблемы существует еще и третий уровень доступности — protected, при котором доступ к члену имеют только производные классы. Что касается внешнего кода, то для него член с уровнем доступности protected идентичен члену с уровнем private.

Помимо уровня защиты для члена еще также может определяться и поведение, связанное с наследованием. Члены базового класса могут быть виртуальными (virtual), а это означает то, что они могут переопределяться в классе, который их наследует. Другими словами, в производном классе для членов может предоставляться альтернативная реализация. Эта альтернативная реализация не подразумевает удаление исходной реализации, которая все равно остается доступной в рамках классах, но от исходного кода она ее действительно закрывает. Если никакой альтернативной реализации не предоставлено, тогда любой внешний код, получающий доступ к члену через производный класс, автоматически использует реализацию этого члена, которая содержится в базовом классе.

Виртуальные члены не могут быть приватными, поскольку это приводит к парадоксу: нельзя говорить, что член может переопределяться в производном классе и одновременно утверждать, что он не должен быть доступен в производном классе.

В примере с классом Animal можно было бы сделать виртуальным метод EatFood() и предоставить для него новую реализацию в каком-то производном классе, например, в Cow, как показано на рис. 8.8. На этом рисунке метод EatFood() отображается и в классе Animal, и в классе Cow; это указывает на то, что у каждого из классов имеется своя собственная реализация данного метода.

Рис. 8.8. Представление виртуального метода в UML

Базовые классы могут также определяться как абстрактные (abstract). Создавать экземпляр абстрактного класса напрямую нельзя; использовать такой класс можно только путем выполнения от него наследования. У абстрактных классов могут быть абстрактные члены, которые не могут иметь никакой реализации в базовом классе и для которых реализация должна предоставляться в производных классах. Например, если бы класс Animal был абстрактным, тогда в UML-пρедставлении он выглядел бы так, как показано на рис. 8.9.

Имена абстрактных классов в UML изображаются с курсивным начертанием (или с использованием пунктирной линии для содержащего их прямоугольника).

На рис. 8.9 методы EatFood() и Breed() отображаются в прямоугольниках абстрактных классов Chicken и Cow; это указывает на то, что они являются либо абстрактными (и тогда подлежат переопределению в других производных классах), либо виртуальными (т.е. уже были переопределены в самих классах Chicken и Cow). Разумеется, абстрактные базовые классы могут предоставлять реализацию для членов, что встречается очень часто. Невозможность создания экземпляра абстрактного класса вовсе не означает, что в нем нельзя инкапсулировать функциональные возможности.

Рис. 8.9. Представление абстрактных классов в UML

И, наконец, еще классы могут быть герметизированными (sealed). Такие классы не могут выступать в роли базового класса и, следовательно, не могут иметь и никаких производных классов.

В языке С# предусмотрен один общий базовый класс для всех объектов, имеющий имя object (которое является псевдонимом класса System.Object из .NET Framework).

Интерфейсы тоже могут наследоваться от других интерфейсов. В отличие от классов, однако, они могут наследоваться от нескольких базовых интерфейсов (тем же образом, каким классы могут поддерживать несколько интерфейсов).

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]