- Lektsia - бесплатные рефераты, доклады, курсовые работы, контрольные и дипломы для студентов - https://lektsia.info -

Виртуальные, динамические методы.



Для того чтобы сделать метод виртуальным или динамическим, надо добавить соответствующую директиву в его описание. Эти методы, в отличие от статических, могут быть перекрытыми (override) в классах наследниках.

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

Для перекрытия метода его надо объявить с директивой override. Заголовок перекрывающего метода должен в точности соответствовать заголовку метода в родительском классе. В программе Method_Binding иллюстрируется специфика виртуальных и перекрытых методов.

Несмотря на то, что только виртуальные и динамические методы могут перекрываться, все методы могут быть перегружены (overload).

Виртуальные и динамические методы являются семантически эквивалентными. Различие между этими методами проявляется только на этапе выполнения в способе реализации собственно вызова метода. Виртуальные методы оптимизированы по скорости вызова, в то время как динамические – по объему кода.

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

Принципиально от статических отличаются виртуальные и динамические методы. Они

объявляются в разделе описания класса добавлением директивы virtual или dynamic

соответственно. Адреса этих методов определяются во время выполнения программы по

специальным таблицам адресов.

Рассмотрим пример. Пусть описывается некоторый класс TData для хранения значения

обобщенного типа и три его потомка для хранения строк, целых и вещественных чисел:

type

TData = class

function Info:string;virtual;abstract;

end;

TStringData = class (TData)

Data:string;

function Info:string;override;

end;

TIntegerData = class (TData)

Data:integer;

function Info:string;override;

end;

TRealData = class (TData)

Data:real;

function Info:string;override;

end;

function TStringData.Info:string;

begin

Info:=Data;

end;

function TIntegerData.Info:string;

begin

Info:=IntToStr(Data);

end;

function TRealData.Info:string;

begin

Info:=FloatToStrF(Data,ffFixed,8,4);

end;В этом примере классы потомки могут только хранить значения соответствующего типа и

выдавать это значение в виде строки. В классе предке нет поля для хранения значения, но описан

абстрактный (abstract), т.е. не требующий реализации в виде программного кода, метод,

одноименный с методами потомков. Если при реализации класса описать и использовать

следующий массив:

var A : array[1..3].TData;

i : integer;

procedure ShowData(Adata:TData);

begin

Form1.Memo1.Lines.Add(AData.Info);

end;

begin

A[1] := TStringData.Create;

A[2] := TIntegerData.Create;

A[3] := TRealData.Create;

...

for i:=1 to 3 do ShowData(A[i])

...

end;

при каждом вызове ShowData(A[i]) будет вызываться не абстрактный метод Info класса TData,

соответствующего типу A[i] по описанию, а тот метод, который соответствует типу A[i] по его

определению в тексте программы. Т.е. одной и той же программной строкой вызываются разные

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

основных принципов объектно-ориентированного программирования - принцип полиморфизма. В

рамках этого принципа при описании однородных классов вначале описывается абстрактный класс

предок, который содержит только интерфейс, а не реализацию одноименных, но различных по

тексту методов классов потомков. В потомках же происходит детализация этих методов. Тогда,

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

потомка, в зависимости от способа создания объекта (метод Create какого класса был вызван)

Допустим, вы имеете дело с некоторой совокупностью явлений или процессов. Чтобы

смоделировать их средствами ООП, нужно выделить их самые общие, типовые черты. Те из них,

которые не изменяют своего содержания, должны быть реализованы в виде статических методов.

Те же, которые изменяются при переходе от общего к частному, лучше облечь в форму

виртуальных методов. Основные, "родовые" черты (методы) нужно описать в классе-предке и затем

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

ShowData, важно лишь, что любой объект, переданный в нее, является потомком TData и он умеет

сообщить о значении своих данных (выполнив метод Info). Если, к примеру, такую процедуру

скомпилировать и поместить в динамическую библиотеку, то эту библиотеку можно будет раз и

навсегда использовать без изменений, хотя будут появляться и новые, неизвестные в момент ее

создания классы-потомки TData!

Наглядный пример использования полиморфизма дает среда Delphi. В ней имеется класс

TComponent, на уровне которого сосредоточены определенные "правила" взаимодействия

Здесь необходимо учесть два момента. Во первых, соответствующие варьируемые методы должны быть объявлены

как виртуальные или динамические. Во вторых, объекту абстрактного класса недоступны поля потомков (т.е. запись

A[i].Data в нашем случае была бы ошибочной).компонентов со средой разработки и с другими компонентами. Следуя этим правилам, можно

порождать от TComponent свои компоненты, настраивая Delphi на решение специальных задач.

Теперь — подросшее о виртуальных и динамических методах. Если задуматься над

рассмотренным выше примером, становится ясно, что у компилятора нет возможности определить

класс объекта, фактически переданного в процедуру showData. Нужен механизм, позволяющий

определить это прямо во время выполнения — это называется поздним связыванием (late binding).

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

механизма служат таблица виртуальных методов (Virtual Method Table, VMT) и таблица

динамических методов (Dynamic Method Table, DMT).

Разница между виртуальными и динамическими методами заключается в особенности

поиска адреса. Когда компилятор встречает обращение к виртуальному методу, он подставляет

вместо прямого вызова по конкретному адресу код, который обращается к VMT и извлекает оттуда

нужный адрес. Такая таблица есть для каждого класса (объектного типа). В ней хранятся адреса

всех виртуальных методов класса, независимо от того, унаследованы ли они от предка или

перекрыты в данном классе. Отсюда и достоинства и недостатки виртуальных методов: они

вызываются сравнительно быстро, однако для хранения указателей на них в таблице VMT

требуется большое количество памяти.

Динамические методы вызываются медленнее, но позволяют более экономно расходовать

память. Каждому динамическому методу системой присваивается уникальный индекс. В таблице

динамических методов класса хранятся индексы и адреса только тех динамических методов,

которые описаны в данном классе. При вызове динамического метода происходит поиск в этой

таблице; в случае неудачи просматриваются таблицы DMT всех классов-предков в порядке

иерархии и, наконец, TObject, где имеется стандартный обработчик вызова динамических методов.

Экономия памяти налицо. Те, для кого это не очевидно или недостаточно, найдут подробности в

разделе данной главы "Как у строен объект изнутри".

Т.о., различие виртуальных и динамических методов состоит только в способе хранения из

адресов. Адрес виртуального метода дальнего предка можно быстро взять из таблицы VMT

данного класса, а для поиска адреса динамического метода дальнего предка придется по очереди

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

память

Директива override указывает что перекрывается динамический или виртуальный метод

класса-предка. Без этой директивы перекрытие перекрывается динамических или виртуальных

методов невозможно.

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

Абстра́ктный ме́тод (или чистый виртуальный метод (pure virtual method - часто неверно переводится как чисто виртуальный метод)) — в объектно-ориентированном программировании, метод класса, реализация для которого отсутствует. Класс, содержащий абстрактные методы, также принято называть абстрактным(там же и пример). Абстрактные методы зачастую путают с виртуальными. Абстрактный метод подлежит определению в классах-наследниках, поэтому его можно отнести к виртуальным, но не каждый виртуальный метод является абстрактным.

Назначение

Абстрактный метод ничего не делает, но определяет параметры и возвращаемое значение.

Назначение абстрактных методов[1]:

· описание абстракции, которая не в более конкретизированном виде не может быть реализована;

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

Delphi

procedure AbstractProcedure; abstract;

Delphi

В Delphi может быть объявлен абстрактный класс с абстрактными методами:

TAbstractClass = class procedure NonAbstractProcedure; procedure AbstractProcedure; abstract;end;

Для такого класса может быть создан объект, но обращение к методу AbstractProcedure этого объекта во время выполнения вызовет ошибку.

В последних версиях Delphi также может быть объявлен абстрактным сам класс:

TAbstractClass = class abstract procedure SomeProcedure;end;

Хотя метод SomeProcedure может быть не абстрактным и реализован в рамках этого класса, создавать объекты объявленного таким образом класса недопустимо.

22. Безопасное преобразование типов

Операция is является бинарной, то есть имеющей два операнда, и принимает следующий вид:

<Объект> is <Класс> .

Она позволяет определить во время выполнения принадлежность Объекта указанному Классу или одному из его потомков. Результатом будет булевская величина, принимающая значение True, если объект совместим по присваиванию с классом Класс, и значение False - в противном случае. Операция is имеет низший приоритет наряду с операциями сравнения =, <>, <, >, <=, >= и операцией in, и поэтому в выражениях заключается в скобки вместе со своими операндами:

if (Sender is TEdit) or (Sender is TMemo) then

Одним из часто встречаемых случаев использования операции is является безопасное преобразование типов, для которого применяется конструкция такого вида:

if <Объект> is <Класс> then <Класс>(<Объект>).<Метод>;

Для демонстрации работы операции is приведем следующий пример.

unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; Button2: TButton; Button3: TButton; CheckBox1: TCheckBox; procedure CommonClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.CommonClick(Sender: TObject);begin if Sender is TButton then Memo1.Lines.Add ('Щелчок на одной из кнопок') else Memo1.Lines.Add ('Щелчок не на кнопках') end; end.

Текст этого примера можно взять здесь.

В этом примере показано выделение компонент-кнопок класса TButton из всего множества компонент формы в случае использования общего для этих кнопок обработчика события OnClick. Для корректной работы примера необходимо с помощью Object Inspector всем компонентам формы Form1 поставить в соответствие один и тот же обработчик события OnClick с идентификатором CommonClick. Если после запуска проекта на выполнение последовательно щелкнуть на форме Form1, поле Memo1, кнопках Button1, Button2, Button3 и контрольном полеCheckBox1, то получим следующий результат:


Операция as предназначена для преобразования типов, у нее так же, как и у операции is два операнда, и ее общий вид можно представить следующим образом:

<Объект> as <Класс> .

Эта операция имеет второй приоритет наряду с операциями типа умножения: *, /, dlv, mod, and, shl и shr.

Результатом операции as будет тот же самый объект, но уже принадлежащий не к своему первоначальному классу, а к указанному в операторе классу Класс.

Главное отличие операции as от прямого преобразования типов вида:

<Класс>(<Объект>)

состоит в том, что при обработке операции as во время выполнения происходит проверка корректности преобразования типов. Если Объект совместим по присваиванию с Классом, то преобразование выполняется. Если же не совместим, то возникает исключительная ситуация, для которой при необходимости можно написать свой обработчик. В случае прямого преобразования типов никаких проверок на совместимость не выполняется.

Операция as используется для безопасного преобразования типов во время выполнения и практически эквивалентна вышеприведенной конструкции с использованием оператора is. Но, в отличие от нее, позволяет писать более компактный и наглядный код, если используется вместе с оператором with:

with <Объект> as <Класс> do

или прямо в квалифицируемом идентификаторе:

(<Объект> as <Класс>).<Поле> (<Объект> as <Класс>).<Метод>

Для демонстрации преобразования типов с помощью операции as возьмем пример, аналогичный вышеприведенному для операции is.

unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; Button2: TButton; Button3: TButton; CheckBox1: TCheckBox; procedure CommonClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.CommonClick(Sender: TObject);begin Memo1.Lines.Add ('Щелчок на ' +(Sender as TControl).Name)end; end.

Текст этого примера можно взять здесь.

В методе TForm1.CommonClick, который выполняет обработку события OnClick для всех компонент формыForm1, происходит преобразование типа объекта-источника события (параметр Sender) к классовому типу TControl. Поскольку класс TControl является предком всех используемых в проекте компонент (Form1, Memo1, Button1, Button2, Button3, CheckBox1), то преобразование выполняется корректно. В результате, один и тот же оператор:

Memo1.Lines.Add ('Щелчок на ' + (Sender as TControl).Name);

выводит в поле Memo1 имена всех компонент, на которых выполняется щелчок мышью. На рисунке 2 показан результат работы проекта, если после его запуска на выполнение последовательно щелкнуть на форме Form1, полеMemo1, кнопках Button1, Button2, Button3 и флажке CheckBox1:


Рис.2. Результат работы приложения

Мы закончили изложение материала по языку Object Pascal. Надеемся, что приведенный справочный материал поможет Вам при программировании в среде Delphi.

Интерфейсы

Интерфе́йс (от лат. inter — «между», и face — «поверхность») — семантическая и синтаксическая конструкция в коде программы, используемая для специфицированияуслуг, предоставляемых классом или компонентом. Интерфейс определяет границу взаимодействия между классами или компонентами, специфицируя определеннуюабстракцию, которую осуществляет реализующая сторона. В отличие от концепции интерфейсов во многих других областях, интерфейс в ООП является строго формализованным элементом объектно-ориентированного языка и в качестве семантической конструкции широко используется кодом программы.