Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Прикладное программирование.doc
Скачиваний:
8
Добавлен:
25.12.2018
Размер:
683.01 Кб
Скачать

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

4.1. Основные понятия

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

Наследование (субклассирование) - это способность одного класса ис­пользовать характеристики другого. Оно использует отношение "вид чего-либо" между двумя классами. Этот простой принцип означает, что если вы хотите создать новый класс, лишь немного отличающийся от известного, то нет необходимости в переписывании заново полей и методов уже сущест­вующих у класса-предка.

Предок (ancestor) - это класс, представляющий свои возможности и ха­рактеристики другим классам через механизм наследования.

Класс, который использует характеристики другого класса посредством наследования, называется его потомком (descendant).

Непосредственный предок, от которого данный класс прямо происходит, называется родителем (parent).

Примечания:

ОР поддерживает модель наследования, известную как простое на­следование, которое ограничивает число родителей конкретного класса од­ним. Другими словами, пользовательский класс может иметь только одного родителя.

Особенностью наследования является то, что:

оно расширяет возможности в том смысле, что любой класс-потомок имеет доступ или наследует практически все ресурсы (методы, поля и свойства) родительского класса и всех его предков до самого верхнего уровня иерархии, т.е. до класса TObject. На­следование поддерживает повторное использование кода у потомков;

наследование позволяет потомку усовершенствовать родительский класс, сделав его более специализированным. В определении класса-потомка указываются лишь новые методы, замещающие методы и новые поля.

В классах-потомках можно переносить объявления из одного раздела в другой, с большей видимостью, кроме раздела Private, из которого в разде­лы с большей видимостью не переносят.

Следует выбирать родительский класс таким образом, чтобы основ­ное соотношение "вид чего-либо" соблюдалось. Например, нельзя сказать "Принтер - это вид дисплея", но можно сказать "Принтер - это вид вы­ходного устройства ".

4.2. Наследование полей

Примечания:

Поля, унаследованные от класса-родителя, располагаются перед но­выми.

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

В классе-потомке можно объявить одноименное поле другого типа и этим скрыть прямой доступ к переопределенному полю. Тем не менее дос­туп будет возможен к обоим полям:

к новому - напрямую:

ОЬ/.<поле>;

к переопределенному - используя приведение типов (typecast):

TParentClass(Obj). <поле>;

При переопределении поля можно перенести его объявление в раздел класса с другой видимостью, однако нельзя перенести объявления полей из раздела Private.

4.3. Поведение методов при наследовании

По тому, какие действия происходят при вызове, методы делятся на че­тыре группы: Static (статические). Virtual (виртуальные). Dynamic (ди­намические), Abstract (абстрактные). Одной из проблем наследования являет­ся диспетчеризация вызовов методов объектов, под которой понимается то, каким образом приложение будет определять какой код требуется выполнить при вызове того или иного метода. Во многом это определяется видом мето­да.

а) Статические методы.

По умолчанию все методы класса являются статическими. Транслятор разрешает вызовы статических методов на фазе трансляции. К моменту за­пуска программы адреса вызова статических методов уже известны и, поэто­му они вызываются быстрее других. Этот механизм называют еще ранним связыванием (early binding).

Примечания:

Статический метод можно скрыть, если определить новый вирту­альный или динамический метод с тем же именем.

Статический метод можно переопределить, если в наследуемом классе объявить новый статический метод с тем же именем. Можно изме­нять при переопределении и список параметров.

Доступ к скрытому или переопределенному статическому методу возможен с помощью механизма приведения типов:

TParentClass(Obj). <метод>;

Вызов статического метода определяется исключительно типом объектной переменной. Тип самого объекта, на который эта переменная в данный момент ссылается, не имеет значения, т.е. при вызове статическо­го метода фактический класс объекта игнорируется1'.

б) Виртуальные методы.

Принципиально от статических отличаются вызовы виртуальных и ди­намических методов. Адреса этих методов определяются лишь во время вы­полнения программы из специальной таблицы. Такой поиск называют еще поздним связыванием (late binding). Решение о вызове конкретного метода решается в процессе выполнения программы, и решение основывается на данных, хранящихся в объекте, вызывающем метод. Таким образом, появля­ется возможность реализовать различные варианты поведения объектов раз­ных классов при вызове методов с одним именем. Это, по сути, определение полиморфизма.

Когда компилятор встречает обращение к виртуальному методу, он под­ставляет вместо обращения к конкретному адресу код-смещение относитель­но начала специальной таблицы, из который и извлекается нужный адрес. Эта таблица называется таблицей виртуальных методов (Virtual Method Table, VMT). Такая таблица есть у каждого класса. В ней хранятся указатели (адреса) всех виртуальных методов класса: вновь объявленных и унаследо­ванных. Отсюда достоинства и недостатки: они вызываются сравнительно быстро, хотя и медленнее статических, однако для хранения указателей тре­буется большее количество памяти. Так, если в базовом классе 5 виртуальных методов и у этого класса 5 производных классов, то фактически будет 30 ука­зателей, содержащих почти идентичную информацию, которые займут 120 байт памяти в куче (Heap).

Доступ к виртуальному методу через VMT - это извлечение адреса по коду (смещению) из таблицы, состоящее из одной машинной инструкции.

Примечания:

Для объявления метода виртуальным служит ключевое слово-дирек­тива Virtual, которое указывается в конце объявления заголовка метода по­сле точки с запятой.

Директива Virtual всегда вводит новый виртуальный метод, никак не

' Если в программе встречается обращение Obj.StaticMethod, где Obj - переменная TClassType, то генери­руется вызов метода TCIassType.StaticMethod или ближайший StaticMethod, наследуемый классом TCIauType. Переменная Obj может ссылаться на один из потомков класса, производного от TClassType, в котором определен свой StaticMethod, т.е. фактически будет использоваться экземпляр дочернего класса. Тем не менее будет вызван именно TClassType.StaticMethod, т.е. для дочернего класса будет вызываться метод родителя связанный с любым другим одноименным методом, наследуемым классом. Переопределенный виртуальный метод ведет себя аналогично переопреде­ленному статическому методу, т.е. полиморфизм не проявляется. Переоп­ределить виртуальный метод в потомке можно и статическим методом.

Type T1=Class

Procedure Test; Virtual;

End;

T2=Class(T1)

Procedure Test; // Test скрывается, но не замещается

End;