Objectl : TClassl; Object2 : TClass2; implementation

// Реализация scex методов пример»

После выполнения создания экземпляров объектов: objectl:=TClassl.Create; Ob-iect2: »TClass2 . Create;

внутренняя структура объектов эти: классов будет иметь вид, представленный ма рис. 3.6.

На рис. 3.6 наглядно видно, чем сличаются между собой различные методы при хранении в реальных объектах в оперативной памяти. Следует отметить, что в экземпляре объекта хранится адре« того места таблицы виртуальных методов, где хранится адрес первого виртуальяого метода. Служебная информация класса хранится по адресам оперативно! памяти с отрицательным смещением. Указанные смещения описаны в фай;е модуля System: vmtSelfPtr = -76;

vmtintfTable * -72;

vmtAutoTable = -68;

vmtlnitTable = -64;

vmtTypelnfo * -60;

vatFieldTable = -56;

vmtMethodTable = -52;

vmtDynamicTable = -48;

vmtClassName = -44;

vmtlnstanceSize = -40;

vmtParent = -36;

vmtSafeCallException = -32 deprecated;//don't use those constants.

vmtAfterConstruction = -28 deprecated;//use VMTOFFSET in asm instead

vmtBeforeDestruction = -24 deprecated;

vmtDispatch = -20 deprecated;

vmtDefaultHandler = -16 deprecated;

vmtNewInstance я-12 deprecated;

vmtFreelnstance = -8 deprecated;

vmtDestroy = -4 deprecated;

vmtQuerylnterface = 0 deprecated;

vmtAddRef = 4 deprecated;

vmtRelease = 8 deprecated;

vmtCreateObject = 12 deprecated;

Работать с этими областями оперативной памяти следует осторожно, поскольку информация в них используется Delphi для управления объектами и их экземплярами. При обращении к константам с директивойdeprecatedкомпилятор выдает предупреждение.

Правила выбора между виртуальными и динамическими методами. J. Если метод, скорее всего, будет перекрыт всеми потомками, его следует сделать виртуальным.

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

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

· Следует учитывать, что самые быстрые и экономичные - статические методы, которые, правда, не обеспечивают позднего связывания.

Абстрактные методы

Иногда попытка создать иерархию классов путем выделения общих признаков приводит к тому, что образуется класс, методы которого реализовывать бессмысленно. Например, если создать класс ТAnimal, который должен объединять общие свойства всех животных, то в нем необходимо описать метод Voice (голос), поскольку большинство животных могут издавать звуки. Для каждого отдельного животного (объектов дочерних классов TAnimal) этот звук известен и можно, перекрыв метод Voice, определить его реализацию. Однако вызов метода Voice для объекта класса ТAnimal бессмыслен, и. следовательно, его реализация тоже. Тем не менее, формально, компилятор требует, чтобы любой метод, описанный в классе, был бы сеализован. Чтобы исключить создание подобного ненужного кода и были предусметрены абстоактные методы.

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

Пример 3.10. Описание абстрактного класса, type

TAnimal = class public

function Voice: string; virtual; abstract; end;

TDog = class (TAnimal) public

function Voice: string; override; function Eat: string; virtual; end;

TCat = class (TAnimal) public

function Voice: string; override; function Eat: string; virtual;

End; Implementation

Function TDog.Voice : string; begin

Result := *Гав-гав'; and;

Function TDog.Eat : string; begin

Result := 'Pedegree';and;

Function TCat.Voice : string; begin

Result := 'Мяу-мяу'; •nd;

function TDog.Eat : string;

Begin

Result 'Wiskas';

and;

Обратите внимание, что программисту не надо описывать реализацию

абстрактного метода TAnimal.Voice.

Object Pascal (в отличие от С++) позволяет создавать экземпляры объектов

класса с абстрактными методами, выдавая при этом предупреждение на этапе компиляции. Посколькуабстрактный метод не имеет реализации, то его можно вызывать только в тех потомках, где он перекрыт, иначе возникнет исключительная ситуация времени выполненияEAbstractError. То есть, >ч;пи описать объект абстрактного класса: <гаг

My Animal : TAnimal; И создатьэкземпляр объектаMyAnimal: MyAnimal := TAnimal.Create;

ro при вызове методаMyAnimal. Voice будет сгенерирована исключительная • и гуация (ошибка), поскольку была попытка вызвать абстрактный метод.

Рассмотрим использование абстрактных методов. КлассTAnimal содержит ииртуальный абстрактный методVoice. Каждый наследник этого класса

перекрывает этот абстрактный метод, а также определяет новый метод Bat. Разница заключается в возможности применять полиморфизм. Например, пусть в тексте программы выполнено присваивание MyAnimal := MyDog;

тогда становится возможным вызовMyAnimal. Voice, который реально выполнит код методаTDog. Voice. А вот вызовMyAnimal .Eat будет также невозможен, поскольку у объектов классаTAnimal не предусмотрен метод с таким названием, то есть для методаEat полиморфизм не действует.

Информация о типах временивыполнения

Рассматривая приведенным выше пример, необходимо отметить,что все-такисуществует возможность вызвать методEat, используя объектMyAnimal.Правда для этого используется уже не полиморфизм, а информацияо типевремени выполнения (RTTI).

Механизм RTTI заключен в двух операцияхдля работыс объектами: is и as.Операцияas фактически осуществляет приведениетипа объекта к другомуклассу, например:MyAnimal as TDog. Даннаяконструкция возвращаетуказатель типаTDog нату область оперативнойпамяти, где расположенэкземпляр объектаMyAnimal. Таким образом, дляобъекта MyAnimalстановится возможным вызов метода классаTDog:(MyAnimal as TDog).Eat

Однако если перед этим не было выполнено присваивание объектуMyAnimalобъекта типаTDog, то при таком вызове произойдет ошибкавременивыполнения. Чтобы избежать этой исключительной ситуации,можноиспользовать операцию is, которая возвращаетtrue, если фактический тип объекта соответствует указанному в операции типу класса, иначеfalse.

if MyAnimal is TDog then

(MyAnimal as TDog) .Eat;

Две операции RTTI,is иas, являются чрезвычайномощными, и их можнорассматривать как стандартные конструкциипрограммирования. Однакоиспользование RTI"I вместо полиморфизмаявляется плохим стилемпрограммированияи приводитк болеемедленным программам, поскольку дляпроверки корректности преобразованиятипов приходится осуществлять обход повсей иерархии классов. Поэтомуследует ограничивать их применениенекоторыми специальными случаями,когда полиморфизмом не обойтись.

§3.9. Перегрузка методов

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

Перегружен может быть любой метод (статический, виртуальный и динамический). Перегружаемый метод описывается с помощью ключевого слова overload (не путать с override). Для виртуальных методов в этом случае нужно дополнительно использовать ключевое слово reintroduce.

Перегрузка методов может осуществляться как внутри одного класса, так и, в отличие от перегрузки функций, на разных уровнях иерархии. Например, перегружаемые методы внутри одного класса:

TMyObject = class (TObject)

function Test (I: integer): string; overload; function Test (S: string): string; overload;

•nd;

Очевидно, что отличие двух методов состоит в типе передаваемого в метод параметра. Теперь если будет создан объект класса TMyObject, то при вызове метода Test будет вызван тот метод, к которому подойдет тип фактического

Параметра.

Пример перегрузки методов на разных уровнях иерархии: type

Tl = class (TObject)

procedure Te3t(X: Integer); overload;

end;

T2 = class (Tl)

procedure Test (S: String); overload;

end;

SomeObject:=T2.Create;

SomeObje-t.Test ('Hello!'); // вызывается T2.Test SomeObject.Test(7); // вызывается Tl.Test

Наши рекомендации