Студопедия КАТЕГОРИИ: АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция |
Виртуальные методы. Конструкторы
Виртуальный (кажущийся, гипотетический) метод имеет спецификатор (стандартную директиву) Virtual. Например: Procedure Met2; Virtual; Виртуальный метод предназначен для переопределения виртуального метода предшествующего предка. Недопустимо смешение статических и виртуальных методов при их переопределении. Ограничение: переопределяющие виртуальные методы должны иметь точно такой же набор формальных параметров, как и самый первый виртуальный метод многоуровневой иерархии объектов. Паскаль обеспечивает вызов (связывание) виртуального метода программы на этапе выполнения программы. Это называют поздним связыванием. При позднем связывании полиморфизм распространяется не только от текущего уровня иерархии вниз, к потомкам (как для статических методов), но и вверх, к предкам. В соответствии с этим основные отличия переопределения виртуальных методов от переопределения статических методов состоят в следующем: 1) если для экземпляра потомка методы предка вызывают другие виртуальные 2) виртуальные методы используют позднее связывание данных с методами, Компилятор не устанавливает связи объекта с виртуальным методом. Вместо этого он создает специальную таблицу виртуальных методов (ТВМ, VMT -Virtual Method Table). Для каждого типа объекта создается своя ТВМ; каждый экземпляр объекта использует эту ТВМ, единственную для данного типа виртуальных объектов. В каждой ТВМ содержится размер данного типа объекта в байтах. ТВМ любого объекта доступна через скрытый параметр Self, содержащий адрес ТВМ, который передается методу при вызове. Связывание каждого экземпляра объекта и его ТВМ осуществляется с помощью конструктора на этапе выполнения программы. Это специальный метод, подобный обычной процедуре, но в заголовке вместо PROCEDURE стоит слово CONSTRUCTOR. Если объектный тип содержит виртуальный метод, то он должен содержать хотя бы один конструктор. Каждый экземпляр объекта должен инициализироваться отдельным вызовом конструктора. Конструктор инициализирует экземпляр объекта и устанавливает для него значение адреса его ТВМ. Экземпляр объекта содержит только адрес ТВМ, а не саму ТВМ. Вызов конструктора должен предшествовать вызову любого виртуального метода для обработки данного экземпляра объекта. Обращение к виртуальному методу до вызова конструктора вызовет ошибку на этапе выполнения программы, так как нет связи экземпляра объекта с его ТВМ. При отладке программы можно использовать директиву компилятора {$R+}. При этом компилятор будет проверять, инициализирован ли экземпляр объекта, вызывающий виртуальный метод. Если в процессе проверки инициализации экземпляра объекта, вызывающего виртуальный метод, будет обнаружен вызов метода до инициализации объекта конструктором, выдается сообщение о фатальной ошибке 210: Object not initialized - объект не инициализирован. Так как директива {$R+} замедляет выполнение программы, после отладки директиву надо удалить; по умолчанию работает директива {$R->. В объекте может быть сколько угодно конструкторов. Конструктор не может быть виртуальным. Конструктор может быть только статическим и может быть переопределен. Конструкторы наследуются так же, как и другие статические методы. Из конструктора можно вызывать и виртуальные методы. Метод конструктора может быть и пустым, так как основная информация содержится не в теле конструктора, а связана с его заголовком, содержащим слово Constructor. Например: Constructor TA.TNIT ; Begin End; Конструктору принято давать имя INIT. На практике в качестве конструктора используют метод, который устанавливает некоторые начальные значения экземпляра объекта. В конструкторе может происходить выделение ОП из кучи, если поля данных динамические, и необходимая инициализация полей данных (в том числе и вызовы конструкторов-предков для унаследованных полей). Пример программы с использованием виртуальных методов дан в листинге 4. Это вариант программы листинга 3, но с виртуальным методом Met2. Листинг 4.Использование виртуальных методов. Program virt1; {$F+,R+} Uses Crt; Type ObjName1 = object { - объявление объекта-предка } Fl1 : integer; Constructor Met1; { - конструктор } Procedure Met2; Virtual; { - виртуальный метод } End; ObjName2 = object(ObjName1){- объявление потомка} Procedure Met2; Virtual; { - виртуальный метод } End; { -- Методы объекта ObjName1 ------ } Constructor ObjName1.Met1; Begin Fl1 := 12; Met2; { - вызов Met2 из конструктора } End; Procedure ObjNamel.Met2; Begin Writeln ( 'Работает метод ObjName1.Met2: FL1 = ', Fl1) End; { -- Методы объекта ObjName2 ------ } Procedure ObjName2.Met2; Begin Fl1 := 34; Writeln ( 'Работает метод ObjName2.Met2: FL1 = ', Fl1) End; Var VI:ObjName1;{- переменная объектного типа - предка } { ------ Основная программа ---------- } Begin ClrScr; Assign (Output, '2virt.res'); Rewrite (Output); Writeln ( 'ОБЪЕКТЫ, ВИРТУАЛЬНЫЕ МЕТОДЫ' ); Writeln ('Работаем с VI - экземпляром типа предка'); VI.Met1; { - вызывается конструктор Met1 для экземпляра VI - предка } { Met1 вызывает метод ObjName1.Met2 - предка } VI.Met2; { - непосредственно вызывается метод ObjName1.Met2; } Writeln ('Работаем с V2 - экземпляром типа потомка'); V2.Met1; { - вызывается конструктор Met1 для экземпляра V2 - потомка VI; Met1 вызывает метод ObjName2.Met2 - потомка -в этом достоинство виртуальных методов } V2.Met2 { - непосредственно вызывается метод ObjName2.Met2; } Close. (Output) ; End. Каждый экземпляр объекта должен инициализироваться отдельным вызовом конструктора. Нельзя инициализировать один экземпляр объекта и затем присваивать этот экземпляр другим, неинициализированным объектам. Другие экземпляры, даже если они содержат правильные данные, не будут инициализированы оператором присваивания и заблокируют систему при любых вызовах их виртуальных методов. Например: Var Obj1, Obj2 : TObj; { - объявление переменных } Begin Obj1.Init; { - инициализация Obj1 } Obj2 := Obj1; { - недопустимо до инициализации Obj2 с помощью Obj2.Init; } End. Хорошим стилем ООП является использование процедур инициализации предков. То есть если потомок имеет новую процедуру инициализации, то в ней обычно сначала вызывается процедура инициализации (например, конструктор) непосредственного предка, а затем выполняется своя. Это естественный способ проинициализировать наследуемые поля предназначенным для этого методом. Дополнительная возможность вызова метода, непосредственно наследуемого объектом-потомком, - использование ключевого слова Inherited (наследуемый) перед именем вызываемого метода. Например, Objl непосредственный предок объекта Obj2. В состав Objl входит метод Metl. Вызов этого метода объекта Objl из метода Obj2.Met2 может быть задан явно в виде: Procedure Obj2.Met2; Begin Inherited Met1 (A1, A2); { Это эквивалентно вызову: Obj1.Met1 (A1, A2); } End; При этом не надо знать имя объекта-предка (Objl). Основные правила использования виртуальных методов: 1) если тип объекта-предка описывает метод как виртуальный, то все его потомки, которые реализуют свой метод с тем же именем, должны описать Error 149: VIRTUAL expected - ОЖИДАЕТСЯ СЛОВО VIRTUAL. 2) если переопределяется виртуальный метод, то заголовок переопределяемо 131: Header does not match previous definition - заголовок не соответствует предыдущему определению; 3) в описании объекта, имеющего виртуальные методы, обязателен -конструктор. Он устанавливает работу механизма виртуальных методов. Вызов виртуального метода без предварительного вызова конструктора может при 4) каждый экземпляр объекта должен инициализироваться отдельным вызовом конструктора; 5) количество конструкторов может быть любым; 6) конструктор должен быть статическим и может быть переопределен. Динамические методы В Паскале имеется дополнительный класс методов позднего связывания -динамические методы. Они являются подклассом виртуальных методов и отличаются от них только способом вызова на этапе выполнения. Объявление динамического метода аналогично виртуальному, за исключением того, что оно должно включать индекс (номер) динамического метода, который указывается сразу за ключевым словом Virtual. Индекс динамического метода должен быть целочисленной константой в диапазоне от 1 до 65535 и представлять собой уникальное значение среди индексов других динамических методов данного объектного типа и его предков. Например: Function GetSum: Real; Virtual 10; где - 10 - ИНДЕКС. Переопределение динамического метода аналогично переопределению виртуального. Оно должно включать слово Virtual, за которым следует тот же индекс, что и в переопределяемом динамическом методе предка. Использование динамических методов целесообразно при создании длинной иерархии объектов с большим количеством виртуальных методов. Если не использовать динамические методы, для них создаются длинные таблицы виртуальных методов с указанием всех виртуальных методов-предков, хотя переопределяться может только часть из них. Это требует большого объема ОП. При использовании динамических методов создается таблица динамических методов (ТДМ, DMT - Dynamic Method Table), альтернативная таблице виртуальных методов. В ней указываются только те виртуальные методы, которые переопределяются. Это экономит ОП, но требуется время для поиска в DMT объектов-предков. Поэтому производительность DMT ниже, чем VMT, так как доступ к методу через VMT - простое извлечение адреса из таблицы, а доступ к методу через DMT может привести к более длительному поиску. Выбор типа метода Прежде чем разрабатывать программу с использованием ООП, надо разработать иерархию типов. Определяя иерархию типов, надо собрать все общие атрибуты, в один тип-предок и позволить остальным типам иерархии наследовать все общие элементы этого типа. При выборе типа метода, статического или виртуального, можно использовать следующее правило: если есть хотя бы малейшая вероятность того, что понадобится из метода предка вызов переопределенного метода потомка, целесообразно делать метод виртуальным. Преимуществом использования виртуальных методов является то, что типы объектов и методы можно определить в модуле и поставить пользователю модуль в виде TPU-файла. Для работы с объектами модуля надо знать содержимое только интерфейсной части модуля. Используя полиморфные объекты и виртуальные методы, пользователь TPU-файла может добавлять новые методы к уже существующим. Добавление новых функциональных характеристик в программу без модификации ее исходного кода (текста) называют способностью к расширению. Это - результат наследования. Позднее связывание позволяет связать новые методы с существующими во время выполнения программы, требуя лишь небольшого увеличения ТВМ. Не следует сомневаться в том, включать или не включать в объект метод, который может быть использован или не использован в конкретной программе. Неиспользуемые методы не влияют ни на быстродействие программы, ни на размер ЕХЕ-файла: если они не используются, то они не включаются в него. В общем случае рекомендуется делать методы виртуальными. Но они требуют ОП для ТВМ. Кроме того, каждый вызов виртуального метода проходит через ТВМ и требует для этого дополнительное время. Использование статических методов имеет смысл, если надо получить оптимальную эффективность программы по скорости ее выполнения и использованию ОП. Статические методы вызываются непосредственно. Поэтому вызов статического метода более быстрый, чем вызов виртуального. Если в объекте нет виртуальных методов, в сегменте данных отсутствует ТВМ и программа будет работать быстрее. Однако в этом случае теряется вариант расширения возможностей на этапе выполнения программы, при позднем связывании. Динамические объекты Основные понятия По способам размещения экземпляров объектов в ОП они могут быть, как и другие переменные, статическими или динамическими. Все приведенные до сих пор объекты были статическими. Они были объявлены в разделе VAR, имели имена и размещались в сегменте данных или в стеке (локальные объекты). ОП для них выделялась до начала выполнения программы. Так же как и любые типы данных в Паскале, объекты могут быть динамическими и размещаться в куче. Работа с динамическим объектом аналогична работе с динамической переменной: надо объявить указатель на объект, создать динамический объект (с выделением ему ОП), обработать и удалить (с освобождением выделенной ОП). После создания динамических объектов с помощью процедуры или функции New значения указателей и динамических переменных можно использовать, например, в операторах присваивания: PObjl := PObj2; - копирование указателя на объект; Pobjl^ := PObj2^; - копирование полей динамического объекта. |
||
Последнее изменение этой страницы: 2018-05-10; просмотров: 213. stydopedya.ru не претендует на авторское право материалов, которые вылажены, но предоставляет бесплатный доступ к ним. В случае нарушения авторского права или персональных данных напишите сюда... |