|
|||||||||||||||||||
|
Виртуальные методыМетод становится виртуальным, если за его объявлением в типе объекта следует новое зарезервированное слово virtual. Запомните, если Вы объявили метод в родительском типе виртуальным, то все методы с таким же именем в любом порожденном типе должны быть так же объявлены виртуальными, чтобы избежать ошибки компиляции. Ниже приведены объекты графической формы, которые как Вы видите виртуализированы надлежащим образом. type Location = Object; X,Y : Integer; procedure Init (InitX, InitY : integer); function GetX : Integer; function GetY : Integer; end; Point = Object(Location) Visuble : Boolean; Constructor Init (InitX, InitY : Integer); procedure Show : virtual; procedure Hide : Boolean; procedure MoveTo (NewX, NewY : Integer); end; Circle = Object (Point) Radius : Integer; Constructor Init(InitX, InitY : Integer; InitRadius : Integer); procedure Show; virtual; procedure Hide; virtual; procedure Expand (ExpandBy : Integer); virtual; procedure Contract (ContractBy : integer); virtual; end; Прежде всего заметим, что метод MoveTo, показанный в последней итерации типа Circle, берется из определения типа Point. Для Circle больше нет необходимости перекрывать метод MoveTo из Point с помощью немодифицированной копии, откомпилированной внутри сферы действия Circle. Вместо этого MoveTo может быть наследована из Point, при этом все вызовы вложенных методов являются методами Circle, а не Point, что было бы верно в иерархии, состоящей только из статических объектов. Кроме того, заметим, что новое зарезервированное слово constructor заменяет зарезервированное слово procedure для процедур Point.Init и Circle.Init. Констрактор - это процедура специального типа, которая делает некоторую работу по настройке для механизма обработки виртуальных методов. Примечание: Каждый тип объекта, который имеет виртуальные методы, должет иметь констрактор (constructor). Более того, констрактор должен вызываться перед вызовом любого виртуального метода. Вызов виртуального метода без предварительного вызова констрактора может вызывать блокирование системы, кроме того, компилятор не имеет способа проверки порядка, в котором вызываются методы. Мы предполагаем использование идентификатора Init для констракторов объектов. Каждый отдельный экземпляр объекта должен инициализироваться с помощью отдельного вызова констрактора. Недостаточно проинициализировать один образец объекта, а затем присвоить этот образец дополнительным образцам. Хотя дополнительные образцы могут содержать правильные данные, они не будут проинициализированы с помощью предложений присваивания и будут вызывать блокирование системы при вызове их виртуальных методов. Например: var QCircle, RCircle : Circle {создает два экземпляра Circle} begin QCircle.Init(600, 100, 30); {вызов констрактора для Circle} RCircle := QCircle; {неверно} end; Что создают констракторы? Каждый тип объекта имеет в сегменте данных таблицу виртуальных методов (ВМТ). ВМТ содержит размер типа объекта и для каждого виртуального метода указатель на код, реализующий этот метод. Констрактор устанавливает связь между экземпляром, вызывающим констрактор и таблицей виртуальных методов данного типа объекта. Важно запомнить: для каждого типа объекта существует только одна таблица виртуальных методов. Отдельные экземпляры типа объекта (т.е. переменная этого типа) содержит связь с таблицей виртуальных методов - они не содержат эту таблицу. Констрактор устанавливает значение этой связи с таблицей виртуальных методов. Вот почему можно запустить выполнение в никуда, если вызвать виртуальный метод до вызова констрактора.
Вызовы проверки допустимого диапазона для виртуальных методов.При разработке программы может возникнуть необходимость использовать преимущества сети безопасности, которую Turbo Pascal помещает ниже вызовов виртуальных методов. Если опция $R находится в активном состоянии {$R+}, то для вызовов всех виртуальных методов проверяется статус инициализации экземпляра, делающего вызов. Если экземпляр, делающий вызов, не инициализируется констрактором, то при выполнении происходит ошибка выхода за допустимый диапазон. Примечание: По умолчанию опция R находится в неактивном состоянии {$R-}. После того, как Вы отладили программу и уверены, что нет вызовов методов из неинициализированных экземпляров, можно в некоторой степени увеличить скорость выполнения программы, установив опцию $R в неактивное состояние {$R-}. Тогда вызовы методов из неинициализированных экземпляров больше проверяться не будут и, возможно, будет происходить блокирование системы, если такие вызовы обнаружены.
Однажды виртуальный, всегда виртуальный.Заметим, что и Point и Circle имеют методы с именами Show и Hide. Все заголовки методов для Show и Hide помечены как виртуальные методы с помощью зарезервированного слова virtual. После того, как родительский тип объекта помечает метод, как виртуальный, все порожденные типы, которые реализуют метод с таким же именем должны помечать этот метод так же виртуальным. Другими словами, статический метод никогда не сможет перекрыть виртуальный метод. Если Вы попытаетесь это сделать, компилятор выдаст ошибку. Нужно всегда помнить, что заголовок метода нельзя изменять каким бы то ни было способом вниз по иерархии объектов, если метод сделан виртуальным. Каждое определение виртуального метода можно представлять воротами для всех таких методов. По этой причине заголовки для всех реализаций одного и того же виртуального метода должны быть идентичными вплоть до количества и типов параметров. Это не распространяется на статические методы. Статический метод, перекрывающий другой метод, может иметь другое количество параметров и другие типы параметров, если это необходимо.
Пример позднего связывания.Чтобы показать как использовать полиморфные объекты с поздним связыванием в программе на Turbo Pascal, вернемся к модулю графических фигур, описанному ранее. Цель состоит в создании модуля, который экспортирует несколько объектов графических фигур (например Point и Circle) и обобщенные методы черчения любой из этих фигур на экране. Модуль, имеющий имя Figures, будет простой реализацией графического пакета, описанного ранее. Чтобы продемонстрировать Figures, построим простую программу, которая определяет новый тип объекта, неизвестный в модуле Figures, а затем использует виртуальные методы для изображения нового типа фигуры на экране. Подумаем о том, чем похожи графические фигуры и чем они отличаются. Различия очевидны, а что касается сходства, то все они включают очертания, углы и кривые, изображенные на экране. В простой графической программе, которую мы будем описывать, фигуры, изображаемые на экране, имеют следующие атрибуты: - они имеют позицию, задаваемую X,Y. Точка внутри фигуры, находящаяся в этой позиции X,Y называется точкой привязки фигуры. - они могут быть или видимыми, или невидимыми, что задается булевским значением True (видимый) или False (невидимый). Если посмотреть ранее приведенные примеры, то перечисленные атрибуты являются точной характеристикой типов объектов Location и Point. Point фактически представляет собой разновидность "дедушкиного" типа, который порождает все объекты графических фигур. Логическое обоснование, разумное объяснение демонстрирует важный принцип объектно-ориентированного программирования: определяя иерархию типов объектов, соберите все общие атрибуты в один тип, и определите иерархию типов так, чтобы все общие элементы наследовались из этого типа. Примечание: Замечание об абстрактных объектах. Тип Point действует как шаблон, из которого все порожденные типы объектов могут взять элементы, общие для всех типов в иерархии. В этом примере объект типа Point не будет фактически изображаться на экране, хотя от этого не будет никакого вреда. (Вызов Point.Show вызовет появление точки на экране). Тип объекта, специально определенного для обеспечения наследования характеристик порожденными объектами, называется абстрактным типом объекта. Главная суть абстрактного типа в том, что он должен иметь порожденные типы, а не экземпляры. Прочитаем Point еще раз, на этот раз как резюме всех атрибутов, которые являются общими для графических фигур. Point наследует X и Y из еще более раннего типа Location, но тем не менее Point содержит X и Y и может передавать их порожденным типам. Заметим, что методы Point не обращаются к форме фигуры, но все фигуры могут быть видимыми или невидимыми и перемещаться по экрану. Point так же имеет важную функцию "передающей станции" для изменений в иерархии объектов в целом. Если добавляются некоторые новые свойства, которые применяются ко всем графическим фигурам (поддержка цвета, например), то их можно добавлять ко всем типам объектов, происходящим от Point, просто добавив эти новые свойства к Point. Тогда новые свойства можно вызывать из любого типа, порожденного Point. Метод для перемещения фигуры в текущую позицию с помощью манипулятора "мышь", можно, например, добавить к Point, не изменяя методы, специфические для каждой фигуры, так как такой метод будет действовать только на поля X и Y. Очевидно, что если новое свойство должно реализовываться различным образом для различных фигур, то должен быть целый набор виртуальных методов, специфических для каждой фигуры, добавляемый в иерархию, при этом каждый метод перекрывает метод, принадлежащий непосредственному родителю. Цвет, например, должен требовать минимальные изменения в процедурах Show и Hide, так как синтаксис многих чертежных подпрограмм Graph.TPU зависит от того, как задается цвет изображения. Предыдущая страница | Следующая страница |
|
Web дизайн: Бурлаков Михаил
Web программирование: Бурлаков Михаил