Наследование и совместимость типов
(расширенная совместимость типов в Object Pascal)
Паскаль – строго типизированный язык, т.е. например нельзя присвоить integer значение типа Boolean( можно использовать приведение типов. Правило: два значения совместимы по типу только тогда, они имеют одинаковый тип, т.е.ссылаются на одно и то же определение типа. )
Из этого правила существует следующее исключение: Если объявить класс, а затем объявить его потомка, то можно присваивать значение класса потомка, значению класса предка. ПРЕДОК=ПОТОМОК надо. ПОТОМОК=ПРЕДОК – нельзя.
Пример:
Type TAnimale = class //супер класс
Public
Constructor Create; // будет иниц. поле Kind
Function CutKind:string;
Private
Kind:string;
End;
TDog=class(TAnimale)
Public
Constructor Create; // будет присв.”Dog”
End;
Форма приложения, использующего эти классы, имеет частное поле myAnimal. Экземпляр этого класса создается при создании формы:
Procedure TformAnimals.FormCreate();
Begin
myAnimal:=TAnimal.Create;
End;
+ Dog + Animal KIND (<-кнопка Button) |
При выборе одного из переключателей происходит след:
Procedure TFormAnimals.RbtnDogClick(….);
Begin
myAnimal.Free;
myAnimal:=TDog.Create;
end; // самая расширенная совместимость типов! ПРЕДОК=ПОТОМОК.
Procedure TAnimals.BtnKindClick(…);
Begin
Kind.label.caption:=myAnimal.GetKind;
End;
Полиморфизм и позднее связывание.
Функции и процедуры паскаля обычно основываются на статическом связывании, которое называется еще ранним связыванием, когда адрес по которому в памяти находится исполняемый код метода определяется на стадии компиляции и компоновки. При этом в точке исполняемого кода, где требуется обратиться к некоторой процедуре или функции подставляется конкретный адрес памяти.
Объектно - ориентированные языки, в том числе ОП, позволяют использовать также динамическое - позднее связывание, в этом случае фактически адрес метода, которому требуется передать управление, определятся не во время компиляции а во время работы программы и зависит от типа объекта, используемого при осуществлении вызова.
Полиморфизм. Благодаря поддержке позднего связывания в Delphi реализуется важная концепция ООП – полиморфизм.
Полиморфизм – способность объектов менять свое поведение в зависимости от того, какому классу принадлежит тот или иной объект.
Пример: имеется переменная которая может ссылаться на объекты разных классов(см. пример выше про расширенную совместимость типов, эту переменную можно назвать полиморфным объектом, или универсальной переменной), для всех этих классов один и тот же метод может быть определен по разному.
В исходных код программы можно добавить обращение к этому методу в отношении объекта на который ссылается эта универсальная переменная.
Какому коду будет передано управление?(какой из версии метода?)-это зависит от того, к какому классу будет относиться объект, на который ссылается переменная в данный момент времени. Это можно определить только на момент выполнения программы.
|
Форма:
о Animal
о Dog
|
MyAnimal:TAnimal;
……..
TFAnim.BtnVoice();
Begin
LbVoice.Caption:=MyAnimal.Voice;
End;
На этапе выполнения вызов MyAnimal.Voice приведет к следующему: если в момент вызова переменная ссылается на экземпляр класса TAnimal, будет вызван TAnimal.Voice, если же на экземпляр класса TDog, то будет вызван TDog.Voice. Такое поведение осуществляется за счет директив virtual и override.
Преимущества: вызов MyAnimal.Voice будет работать для экземпляра любого класса, который является потомком TAnimal, даже если он написан в другом модуле и даже если потомок вообще еще не написан. Чтобы определить вызов метода совместимым со всеми потомками базового класса компилятору не требуется никакой информации об этих потомках, ему требуется только класс предок.
Чтобы перекрыть виртуальный метод в классе потомке используется директива override. Чтобы перекрыть статический метод мы просто пишем метод с тем же самым методом. Можно вместо статического метода в потомке определить виртуальный метод с таким же именем, но они не будут уже никак связаны.
Type myclass = class
Procedure One; virtual;
Procedure TWD;
End;
mySubClass = class(myclass)
procedure one; override;
procedure Two;
end;
Существует 2 способа перекрытия методов:
1. Предполагает полную замену родительского метода
2. Заключается в добавлении некоторого кода.
mySubClass.One
…новый код…
Inherited;
End;
Override – использование этого слова позволяет компилятору проверить соответствие методов родительского и дочернего класса. Если перекрывается существующий виртуальный метод, то необходимо использовать тот же набор параметров. Если в рамках класса потомка создается новая версия метода можно использовать любой другой набор параметров, но в результате получается новый метод, никак не связанный одноименным методом в классе потомке.
В Delphi(в обжект паскаль) поддерживается 2 различных варианта реализации позднего связывания.
1. Использование директивы virtual.
2. Можно использовать dynamic.
Синтаксис и результат выполнения в обоих случаях одинаковые. Различия заключаются во внутреннем механизме, используемом компилятором для реализации позднего связывания.
Обращение к любому виртуальному методу осуществляется с использованием таблиц виртуальных методов (VMT), в которой хранятся адреса всех виртуальных методов этого класса. Чтобы передать управление некоторому виртуальному методу компилятор генерирует код выполняющий переход по адресу, хранящемуся в той ячейке VMT, которая соответствует вызываемому виртуальному методу.
VMT обеспечивают быстрое выполнение вызова. Их недостаток – они требуют отдельной ячейки для каждого виртуального метода, каждого дочернего класса, даже если этот метод не был перекрыт в этих классах. И таким образом адрес виртуального метода, определенного в базовом классе будет скопирован в таблицы VMT всех классов, которые являются производными от данного базового класса. Если иерархия классов достаточно глубокая и разветвлённая для хранения адресов одних и тех же методов в разных таблицах VMT может потребоваться большой объем памяти.
Динамические методы. Чтобы осуществить вызов динамического метода используется уникальный номер, при помощи которого идентифицируется метод, поиск адреса соответствующей функции как правило осуществляется медленнее чем простой одношаговый поиск в таблице VMT.Преимущество динамического метода в том что информация о нем записывается в дочерний класс только в случае, если метод был перекрыт в этом классе. Для больших и глубоких иерархий такой способ может привести к значительной экономии памяти при некоторой потере быстродействия.
….абстрактные методы
Abstract, нужны для полиморфизма.
Type Tanimal=class
……………
Function Voice:string;virtual;abstract;
Private
Kind:string;
End;
TDog = class(Tanimal);
Function Voice:string;override;
Function Eat:string; virtual;
…..
End;
TCat = class(TAnimal)
……….
Function Voice:string; override;
Function Eat:string; virtual;
….
End;
Каждый наследник Tanimal переопределяет виртуальный абстрактный метод Voice и добавляет новый метод Eat.
LbVoice.Caption:=myAnimal.Voice;
LbEatCaption:=myAnimal.Eat;// error!
Для решения данный проблемы переменная myAnimal содержит либо TCat либо TDog, соответ.треб.вызвать их метод Eat. Можно воспользоваться механизмом определения типа на этапе выполнения RTTI RunTime Type Information и с его помощью привести объект типа Tanimal к типу TDog или TCat, в зависимости от того, чем он сейчас является на самом деле.
Но добавление абстрактного метода в суперкласс является типичным решением такой проблемы.
Дата добавления: 2016-07-27; просмотров: 1661;