Наследование и совместимость типов


(расширенная совместимость типов в 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 реализуется важная концепция ООП – полиморфизм.

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

Пример: имеется переменная которая может ссылаться на объекты разных классов(см. пример выше про расширенную совместимость типов, эту переменную можно назвать полиморфным объектом, или универсальной переменной), для всех этих классов один и тот же метод может быть определен по разному. В исходных код программы можно добавить обращение к этому методу в отношении объекта на который ссылается эта универсальная переменная. Какому коду будет передано управление?(какой из версии метода?)-это зависит от того, к какому классу будет относиться объект, на который ссылается переменная в данный момент времени. Это можно определить только на момент выполнения программы.
Пример пример: Имеются два класса Tanimal и Tdog, в них определен метод, имеющий позднее связывание, применим его к универсальной переменной MyAnimal, которая в процессе выполнения программы будет ссылаться то на экземпляр класса Tanimal, то на экземпляр класса TDog.   TAnimal = class Public Function Voice:string;virtual; End; Tdog = class(TAnimal) Public Function Voice:string;override;   Function TAnimal.Voice:string; Begin Voice:=’voice Animal’; End;   Function TDog.Voice:string; Begin Voice:=’Gav-Gav’; End;

Форма:

о Animal
voice


о Dog

Gav Gav

 


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; просмотров: 1553;


Поиск по сайту:

Воспользовавшись поиском можно найти нужную информацию на сайте.

Поделитесь с друзьями:

Считаете данную информацию полезной, тогда расскажите друзьям в соц. сетях.
Poznayka.org - Познайка.Орг - 2016-2024 год. Материал предоставляется для ознакомительных и учебных целей.
Генерация страницы за: 0.013 сек.