Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Медведев_С++_CLI_C#_Java_J#.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
5.17 Mб
Скачать

6.Наследование классов и интерфейсы

6.1Об интерфейсах

Под интерфейсом объекта понимается совокупность всех его открытых (public) функций, свойств и событий. Составляющие интерфейс функции, свойства и события (о событиях и свойствах поговорим попозже) используются при применении объекта.

Языки C++/CLI и C# содержат конструкцию, описывающую интерфейс как совокупность функций, свойств и событий. В языке же Java интерфейс определяется как совокупность функций, свойств и статических переменных.

Используя наследование, интерфейсы связывают с классами. Будучи наследованным несколькими разными классами, интерфейс обязывает эти классы реализовать объявленные в нём функции, свойства и события, то есть обязывает их подчиняться установленным этим интерфейсом правилам поведения.

Таким образом, объекты, созданные из разных классов, наследующих один и тот же интерфейс, в определённых ситуациях должны вести себя одинаково.

Например, объекты языка C#, наследующие интерфейс IDisposable, обязательно имеют функцию Dispose() и событие Disposed, которые применяются при освобождении ресурсов. Класс любого объекта, оперирующего с ресурсами, должен наследовать этот интерфейс.

Наследование интерфейса Observer в языке Java добавляет наблюдающим объектам возможность обозревать наблюдаемые объекты, то есть включает объекты в особый механизм их взаимосвязи.

В отличие от классов из интерфейсов нельзя создать объекты. Они для этого и не предназначены. Назначение интерфейсов состоит в формировании поведения объектов путём навязывания классу объекта обязательной реализации указанных в интерфейсе функций, свойств и событий.

Подробнее о свойствах и событиях будет изложено в разделе 8.

6.2Наследование неуправляемых классов в C++/CLI

Объектно-ориентированное программирование использует механизм наследования типов, с помощью которого программист на базе существующих типов может получать (порождать или наследовать) новые типы. При этом новый тип может наследовать как данные, так и функции базового типа, из которого он порождается. То есть создаётся иерархия типов.

Порождение (наследование) классов - это средство получения новых классов на базе существующих. При этом новый класс наследует данные и функции из базовых классов и интерфейсов.

C++/CLI. В языках С++ и C++/CLI объявление порождённого неуправляемого класса начинается с ключевого слова class, за которым указывается имя порождённого класса. Затем за двоеточием следует список порождения, состоящий из отделённых запятыми имен базовых классов или интерфейсов с предшествующими им видами порождения public, private, protected). После этого заголовка следует тело порождённого класса, содержащее объявления данных и функций.

class имя-порожд-класса :

{ public | private | protected } имя-баз-класса | имя-баз-интерфейса

[, { public | private | protected } имя-баз-класса | имя-баз-интерфейса] ...

{

объявление данных и функций

};

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

class имя-класса; // Упреждающее объявление класса имя-класса

Ключевое слово public (открытый), private (закрьггый) или protected (защищённый) перед именем базового класса в списке порождения объявляет доступ к наследуемым членам базового класса в порождённом классе. По умолчанию вид порождения - private. Для интерфейсов в списке порождения применяется только доступ public. Наследуемый интерфейс может включать только интерфейсные функции.

Следующая таблица определяет доступ в порождённом классе к наследуемым членам (данным и функциям) базового класса:

Доступ в базовом классе

Доступ при порождении (в списке порождения)

public

protected

private

public

public

protected

private

protected

protected

protected

private

private

недоступен

недоступен

недоступен

Анализ таблицы показывает, что

  • члены базового класса с доступом private становятся недоступными в порождённом классе. Их использование в. порождённом классе возможно только через функции базового класса, которые доступны в порождённом классе;

  • доступ при порождении public позволяет "протаскивать" члены базового класса с доступами public и protected по иерархии порождения; при этом члены с доступом public будут доступны и вне объекта, а с доступом protected будут защищены и могут использоваться только внутри объекта;

  • доступ при порождении private закрывает члены базового класса для дальнейшего использования.

Диаграммы классов языка UML (рис. 6.2.1) поясняют графически приведённую выше таблицу, указывая в блоке комментария доступ к наследуемым данным а , e и с базового класса А в порождённом классе В при разных видах наследования. Символ "-" означает доступ private, символ "+" означает доступ public и символ "#" - protected.

Рис. 6.2.1. Доступ к наследуемым данным и функциям базового класса в языке С++

На другой диаграмме языка UML (рис. 6.2.2) показано изменение доступа к данным а, в и с базового класса А в иерархии порождения: класс В наследует класс А с порождением public, класс С наследует класс В с порождением protected и, наконец, класс D наследует класс С с порождением public.

Рис. 6.2.2. Изменение доступа к наследуемым членам (данным и функциям) базового класса при порождении в языке С++

Последовательность порождений на рисунке 6.2.3 говорит, что при использовании в иерархии только одного вида порождения public способствует "протаскиванию" данных базового класса с доступом public и protected вдоль всей иерархии наследования неизменяемыми.

Рис. 6.2.3. "Протаскивание" данных базового класса с доступом public и protected вдоль всей иерархии наследования в языке С++

В дальнейшем, при разработке Windows-приложений мы будем создавать наши классы, порождая их из классов библиотеки .NET Framework, содержащих десятки данных и функций. А наш класс добавит к ним ничтожное количество переменных и функций, обусловленных особенностями решаемой задачи. На основе порождённых классов создаются объекты с необходимым поведением. Взаимосвязь между классами и объектами может осуществляться как на уровне защищённых (protected) данных и функций внутри иерархии порождения, так и с помощью специальных функций, способных связать иерархически не связанные объекты.