Набрали: Валентин Буров, Илья Тюрин

Вид материалаЛекция
Подобный материал:
1   ...   11   12   13   14   15   16   17   18   19

Java


В Java наследование только единичное:

class Y extends X {

...

};


Но еще в Java введен тип данных – interface, строго говоря, это совокупность типов данных.

interface имя { описание членов интерфейса};

Член интерфейса – это либо описание метода (без тела), либо константы (static final int i=0).

В Java есть понятие чисто виртуальной функции (вспомним, что в Java все функции виртуальны). У таких функций нет реализации. Интерфейс – это интерфейс, он сам по себе ничего реализовывать не должен. Но тем не менее реализован он все-таки должен быть. Поэтому общая форма наследования в Java выглядит так (следует заметить, что в Java все классы являются потомками класса Object, поэтому, если опустить “extends X” в нижеследующем примере, то Y будет непосредственным потомком Object):


class Y extends X implements <список_интерфейсов> { ...}


Если класс реализует все функции из списка интерфейсов, то это – неабстрактный класс. Если не все – абстрактный (чтобы из него можно было вывести что-то еще).

Также как и в C++ в Java нельзя создавать экземпляры АК. Но ссылки и указатели задавать можем.

Очень много понятий реализуется через интерфейсы. Например, проблема копирования. В C++ эта проблема решена через перекрытие операции присваивания и конструктора копирования. В Java же вместо этого делается так: есть

объект Object

и

interface Cloneable;

Object может реализовывать Cloneable, чтобы сделать свой объект клонируемым следует переопределить методы из Cloneable, поскольку

Object implements Cloneable.

Таким же образом можно запретить копирование или реализовать его нестандартно (стандартно – это побитово).

Понятие интерфейса – это и есть понятие АТД. Причем добавляется гибкость и, как бы побочно, минимизация времени перекомпиляции – такого даже в Ada добиться не удавалось.


Лекция 25


В прошлый мы рассмотрели понятие чистых абстрактных классов. Есть также очень важное понятие чистой виртуальной функции:


virtual void f()=0;


Такая функция предназначена только для обозначения места в интерфейсе и ее основное назначение быть переопределенной в классах, производных от данного класса. Классы, в которых есть чистые виртуальные функции называются абстрактными классами. Это не то же самое, что абстрактный тип данных (АТД). АТД – это такой тип данных, который доступен только через функциональный интерфейс. В абстрактном классе ничего о скрытии деталей реализации не говорится. Абстрактный класс предназначен только для того, чтобы быть базовым в некоторой иерархии. На самом деле программирование в терминах абстрактных классов можно сопоставить с программированием и АТД, особенно применительно к множественному наследованию.

В чисто абстрактном классе (ЧАК) нет данных (за исключением статических), и кроме того, все функции члены являются чисто виртуальными. Т.е. ЧАК представляется только ссылкой на ТВМ (Таблица Виртуальных Методов). Оказывается, что ЧАК можно рассматривать как аналог интерфейса АТД. Поэтому между этими понятиями есть некоторое соответствие. При этом в роли реализации выступает класс, который наследует ЧАК.

З
десь может оказаться очень полезным множественное наследование. Если мы хотим реализовать класс, представляющий собой множество, реализованное на базе линейного списка то это можно сделать следующим образом. Нужно создать ЧАК Set, который содержит чисто виртуальные функции Insert, Delete, и т.д. Затем создать абсолютно неабстрактный класс (а скорее АТД) List, который предоставляет средства для работы со списком. И наконец, нужно создать класс List_Set, который является наследником классов Set и List и переопределяет функции класса Set. При этом никаких проблем с множественным наследованием не возникает, потому что класс Set не содержит никаких данных, т.е. наследование данных происходит единичным образом.

При этом класс List_Set может попутно реализовывать и другие абстракции, например стек, если унаследует соответствующий ЧАК Stack (тогда имя этого класса следует сделать другим, например List_Generic). Тогда объект этого класса сможет вести себя и как множество, и как стек, и как другие абстракции (если они будут реализованы). Главное, чтобы данные наследовались только единожды, чтобы не возникало проблем с множественным наследованием.

Возникает концепция множественных интерфейсов, которая сейчас очень популярна с точки зрения программирования. Например, идеология COM-объектов (Component Object Module) фирмы Microsoft как раз использует парадигму множественных интерфейсов. Любой COM-объект поддерживает некоторые интерфейсы, которые, с точки зрения С++, являются ЧАК. Сейчас существуют десятки интерфейсов, более того, программист может сам написать свой интерфейс при написании своего COM-объекта. Компонентное программирование сейчас поддерживают различные фирмы, поскольку это очень удобная штука. СОМ-программирование основывается на технологии OLE-2. OLE-2 как раз описывает механизмы реализации интерфейсов. С языковой точки зрения, компонентное программирование очень хорошо укладывается в концепцию ЧАК, и множественного наследования по одному направлению. В языке Java в чистом виде реализована эта концепция. Java стандартизует существующую практику программирования. Классическое наследование реализуется с помощью ключевого слова extends. Кроме того, есть еще ключевое слово implement, которое позволяет множественно наследовать интерфейсы.


class A extends B

implements список_интерфейсов


Класс А может стать абстрактным классом, если он что-то из этих интерфейсов недореализует. Сам интерфейс определяется так:


interface имя {

описание

}


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

Обратите внимание, что с точки зрения реализации языке С++, наличие виртуальных методов практически не добавляет накладных расходов. Накладные расходы будут при вызове виртуального метода, но не такие, как, например в языке Smaltalk, когда вызов метода требует пробежки по всему списку объектов. С точки зрения памяти, накладных расходов также немного, поскольку к объекту добавляется только ссылка на ТВМ.

Какие методы делать виртуальными? Конструкторы в языке С++ виртуальными по определению быть не могут. Для некоторых классов виртуальность рассматривать вообще не стоит (например, для класса Complex). Если в классе появляются виртуальные методы, то становится заведомо ясно, что стоит объявить виртуальным деструктор (хотя есть возможность в С++ не объявлять его виртуальным). Пусть в классе Х есть виртуальная функция f(), и пусть сам класс Х никаких ресурсов, которые нужно освобождать, не захватывает. Стоит ли писать деструктор? Не только стоит, но и нужно его объявить виртуальным. Известно, что этот класс будет вершиной некоторой иерархии (иначе ни имело бы смысла говорить о виртуальности). У этого класса может появиться наследник, который будет производить захват ресурсов, и следовательно, программист напишет для него некий деструктор. Если деструктор класса Х не будет виртуальным (или его вообще не будет), то деструктор наследника не будет вызван, например в таком коде:


X* px;

… //здесь px меняет свой динамический тип

delete px; // вызывается деструктор класса Х, который ничего не делает


Если же деструктор класса Х будет виртуальным, то вызовется нужный деструктор. Поэтому деструкторы в таких случаях всегда нужно делать виртуальными.