Связь и интернет Архив Программирование
   
Сделать стартовойСделать закладку            
   ПОИСК  
   
Главная / Pascal и Delphi / Borland Pascal / Объектно-ориентированное программирование /
8  Perl
8  PHP
8  JavaScript
8  HTML
8  DHTML
8  XML
8  CSS
8  C / C++
8  Pascal и Delphi
8  Турбо Ассемблер
8  MySQL
8  CASE-технологии
8  Алгоритмы
8  Python
8  Обратная связь
8  Гостевая книга
Новости о мире


Cтатические методы - Программирование от RIN.RU
Cтатические методы

Все показанные до сих пор методы, связанные с типами объектов 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.


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


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



 8  Комментарии к статье  8  Обсудить в форуме  8  Обсудить в чате

8  В тему

Объекты

Наследование

Объекты: наследующие записи

Tехника программирования

Методы

Совмещенные код и данные

Определение методов

Объекты и модули

Программирование в "действительном залоге"

Инкапсуляция

Методы: никакого ухудшения

Расширяющиеся объекты

Полиморфизм

Совместимость типов объектов

Полиморфические объекты

Виртуальные методы

Вызов виртуальных методов

Статические или виртуальные методы?

Динамические объекты

Размещение и инициализация

Удаление динамических объектов

Деструкторы

Пример размещения объекта

Что же дальше?

 
  
  
    Copyright ©  RIN 2003 - 2004      * Обратная связь