Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Виртуальные, динамические методы.
Для того чтобы сделать метод виртуальным или динамическим, надо добавить соответствующую директиву в его описание. Эти методы, в отличие от статических, могут быть перекрытыми (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, то получим следующий результат:
Эта операция имеет второй приоритет наряду с операциями типа умножения: *, /, 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:
Мы закончили изложение материала по языку Object Pascal. Надеемся, что приведенный справочный материал поможет Вам при программировании в среде Delphi. Интерфейсы Интерфе́ йс (от лат. inter — «между», и face — «поверхность») — семантическая и синтаксическая конструкция в коде программы, используемая для специфицированияуслуг, предоставляемых классом или компонентом. Интерфейс определяет границу взаимодействия между классами или компонентами, специфицируя определеннуюабстракцию, которую осуществляет реализующая сторона. В отличие от концепции интерфейсов во многих других областях, интерфейс в ООП является строго формализованным элементом объектно-ориентированного языка и в качестве семантической конструкции широко используется кодом программы. Популярное:
|
Последнее изменение этой страницы: 2016-07-14; Просмотров: 818; Нарушение авторского права страницы