TURBO PASCAL

Новости

Программы   

Turbo Pascal 

Игры

Документация   

Странности

FAQ

Ссылки

Форум

Гостевая книга

Рассылка

Благодарности

Об авторе

 

 

Наследование статических методов 

             Все показанные до сих пор методы,  связанные с типами объек-
        тов TEmployee,  THourly, TSalaried и TCommissioned, являются ста-
        тическими  методами.  Однако,  со  статическими  методами связана
        пpоблема наследования.

             Для того, чтобы разобраться с этой проблемой, отложим в сто-
        рону  наш пример с платежной ведомостью и рассмотрим другой упро-
        щенный и нереалистичный, но показательный пример. Вернемся к кры-
        латым насекомым.  Предположим, что нужно создать программу, кото-
        рая будет рисовать на экране различные типы  летающих  насекомых.
        Предположим,  вы решили, что на вершине иерархии будет находиться
        объект Winged. Пусть вы планируете, что новые типы объектов лета-
        ющих насекомых как будут строиться как потомки Winged.  Например,
        вы можете создать тип объекта Bee,  который отличается  от  родс-
        твенных крылатых насекомых тем, что имеет жало и полосы. Конечно,
        у пчелы есть другие отличающие ее характеристики, но в нашем при-
        мере это может выглядеть следующим образом:


             type
                 TWinged = object(Insect)
                   procedure Init(AX, AY: Integer) { инициализирует
                                                    экземпляр }
                   рrocedure Show;   { отображает крылатое насекомое на
                                       экране }
                   рrocedure Hide;   { стирает крылатое насекомое с
                                       экрана }
                   рrocedure MoveTo(NewX, NewY : Integer);
                                     { перемещает крылатое насекомое }
             end;

             tyрe
               TBee = object(Winged)
                .
                .
                .
                рrocedure Init(AX, AY: Integer) { инициализирует
                                                  экземпляр Bee }
                рrocedure Show;          { отображает пчелу на экране }
                рrocedure Hide;          { стирает пчелу с экрана }
                рrocedure MoveTo(NewX, NewY : Integer);
                                         { перемещает пчелу }
             end;

             И TWinged,  и TBee имеют по четыре  метода.  TWinged.Init  и
        TBee.Init инициализируют экземпляр соответствующих объектов.  Ме-
        тод  TWinged.Show  знает,  как  рисовать  крылатое  насекомое  на
        экране,  а метод TBee.Show - как рисовать пчелу (крылатое насеко-
        мое с полосками на теле и с жалом). Метод TWinged.Hide знает, как
        стирать  крылатое  насекомое  с  экрана,  а метод TBee.Hide - как
        стирать пчелу.  Два метода Show отличаются друг от  друга,  равно
        как и два метода Hide.

             Однако, методы TWinged.MoveTo и TBee.MoveTo полностью одина-
        ковы. В нашем примере X и Y определяют положение на экране.

             рrocedure TWinged.MoveTo(NewX, NewY: Integer);
             begin
               Hide;
               X := NewX;    {новая координата X на экране}
               Y := NewY;    {новая координата Y на экране}
               Show;
             end;

             рrocedure TBee.MoveTo(NewX, NewY: Integer);
             begin
               Hide;
               X := NewX;    {новая координата X на экране}
               Y := NewY;    {новая координата Y на экране}
               Show;
             end;

             Не изменилось ничего,  кроме копирования программы и  поста-
        новки  квалификатора  TBee перед идентификатором MoveTo.  Так как
        методы одинаковы,  зачем нужно помещать MoveTo в TBee?  Ведь  Bee
        автоматически  наследует  MoveTo  от  TWinged.  Поэтому  не нужно
        переопределять метод MoveTo из TWinged,  но это именно то  место,
        где возникает проблема в случае статических методов.

             Термин "статический" был выбран для описания методов, не яв-
        ляющихся виртуальными - термин,  который мы введем далее.  Факти-
        чески,  виртуальные  методы  являются решением этой проблемы,  но
        прежде чем понять решение,  вам следует разобраться в самой проб-
        леме.

             Признаки проблемы  состоят  в  следующем:  пока копия метода
        MoveTo не будет помещена в область действия TBee  для  подавления
        метода MoveTo объекта TWinged, метод не будет работать правильно,
        если он будет вызываться из объекта типа TBee. Если TBee запуска-
        ет метод MoveTo объекта TWinged,  так то, что движется по экрану,
        является крылатым насекомым, а не пчелой. Только когда TBee вызы-
        вает копию метода MoveTo, определенного в его собственной области
        действия, на экране с помощью вызовов Show и Hide будут рисовать-
        ся и стираться пчелы.

             Почему это так? Это объясняется способом, которым компилятор
        разрешает вызовы методов. Когда компилируются методы Bee, то сна-
        чала  встречаются TWinged.Show и TWinged.Hide и их код компилиру-
        ется в сегмент кода.  Немного позднее в файле  встречается  метод
        Winged.MoveTo,  который вызывает TWinged.Show и TWinged.Hide. Как
        и при вызове  любой  процедуры,  компилятор  замещает  ссылки  на
        TWinged.Show и TWinged.Hide в исходном коде на их адреса,  сгене-
        рированные в сегменте кода.  Таким образом,  когда вызывается код
        TWinged.MoveTo,  он,  в  свою  очередь,  вызывает  TWinged.Show и
        TWinged.Hide со всеми вытекающими последствиями.

             До сих пор это был типичный для Borland Pascal сценарий и он
        был бы справедлив (за исключением номенклатуры), начиная с версии
        1.0 Turbo Pascal 1983 года. Однако, дело меняется, когда вы вклю-
        чаете в этот сценарий принцип наследования.  Когда TBee наследует
        метод от TWinged,  он (TBee) использует метод в точности так, как
        тот был откомпилирован.

             Снова посмотрите,  что должен наследовать TBee, если он нас-
        ледует TWinged.MoveTo:

             рrocedure TWinged.MoveTo(NewX, NewY: integer);
             begin
               Hide;                       { Вызов Winged.Hide }
               X := NewX;
               Y := NewY;
               Show                        { Вызов Winged.Show }
             end;

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

        факт, что если Bee вызывает метод TWinged.MoveTo, то он также вы-
        зывает TWinged.Show и TWinged.Hide,  а не TBee.Show и  TBee.Hide.
        Поскольку   TWinged.MoveTo   вызывает   методы   TWinged.Show   и
        TWinged.Hide, TWinged.MoveTo нельзя наследовать. Вместо этого, он
        должен быть  переопределен своей второй копией,  которая вызывает
        копии Show и Hide,  определенные внутри области  действия  второй
        копии, то есть, TBee.Show и TBee.Hide.

             При разрешении вызовов методов,  логика компилятора работает
        так: при вызове метода компилятор сначала ищет метод, имя которо-
        го  определено внутри типа объекта.  Тип TBee определяет методы с
        именами Init,  Hide,  Show и MoveTo.  Если метод TBee должен  был
        вызвать  один  из этих четырех методов,  то компилятор заменил бы
        вызов на адрес одного из собственных методов Bee.

             Если в типе объекта не определен метод с  таким  именем,  то
        компилятор поднимается выше к непосредственному родительскому ти-
        пу в поисках метода с указанным именем. Если метод с таким именем
        найден, то адрес родительского метода замещает имя в исходном ко-
        де дочернего метода. Если метод с таким именем не найден, то ком-
        пилятор  продолжает продвигаться вверх по родительским объектам в
        поисках метода.  Если компилятор наталкивается  на  самый  первый
        (высший) тип объекта, то он выдает сообщение об ошибке, указываю-
        щее, что ни одного такого метода не определено.

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

Содержание

На первую страницу

Rambler's Top100 Rambler's Top100
PROext: Top 1000

(с)Все права защищены

По всем интересующим вопросам прошу писать на электронный адрес

Hosted by uCoz