Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
курс лекций программирование.doc
Скачиваний:
40
Добавлен:
31.08.2019
Размер:
5.03 Mб
Скачать

Тема 4.4.Виртуальные методы и полиморфизм.

Тип лекции: текущая

План:

1. Виртуальные методы и полиморфизм. Полиморфизм предполагает определение класса или нескольких классов для родственных объектных типов так, что каждому классу отводится своя функциональная роль. Методы одного класса обычно наделяются общим именем. В ситуации когда необходимо сложный метод использовать в нескольких объектах и различия в поведении объектов минимальны, возможно создание смежного сложного метода с вынесением различий в сменные подчиненные методы. Такой метод называется конструктивным полиморфизмом. Осуществляется эта идея созданием виртуальных сменных методов. В заголовке такого метода присутствует слово virtual, а для их подключения к общему методу - обращение к конструктору - блоку со специальным заголовком.    constructor <имя блока> (<список формальных параметров>) К конструктору надо обращаться для каждого объекта использующего виртуальные методы. Задача: тип kom - сын типа pozicia представляет закрашенные квадраты с длиной стороны raz (в пикселах). Наследуемые поля x, y являются координатами центра квадрата. Процедура kom.zoom увеличивает (уменьшает) объект если аргумент znak>0 (znak<=0). Длина raz изменяется на 2*delt, где delt - еще один аргумент. uses graph, crt; type pozicia = object       x, y: integer;       procedure init (xn, yn: integer);       procedure locat (var xl, yl: integer);    end;    kom=object (pozicia)       cvet, raz: word;       procedure init (xn, yn: integer; color: word);       procedure zoom (delt, znak: integer);    end; procedure pozicia.init; begin    x:=xn;    y:=yn; end; procedure pozicia.locat; begin    xl:=x;    yl:=y; end; procedure kom.init; begin    pozicia.init (xn, yn);    cvet:=color;    raz:=1; end; procedure kom.zoom; var j, d: integer; begin    if znak>0 then setcolor (cvet)       else setcolor (getBkcolor);    for j:=1 to delt do       begin          if znak>0 then raz:=raz+2;          d:=raz div 2;          moveto (x-d, y-d);          linerel (d+d, 0);          linerel (0, d+d);          linerel (-d-d, 0);          linerel (0, -d-d);          if (znak<=0) and (raz>1) then raz:=raz-2;       end; end; const n=50; var j, d, r, xx, yy: integer;       kvad: array [1..n] of kom; begin       d:=detect;       randomize;       initgraph (d, r, 'c:\tp\bgi');       for j:=1 to n do          kvad[j].init (random(GetMaxX), random(GetMaxY), random(GetMaxColor);       repeat          delay (100);          j:=random (n)+1;          kvad[j].zoom(random(8)+1, random(3)-1);       until kepressed;       closegraph;       kvad[1].locat (xx, yy);       write (xx, ' ', yy);       readln;

Давайте вернемся к методу Move из предыдущей программы. Вот его описание.

procedure Dot . Move;

begin

Hide;

a : =a+Da; b : =b+Db;

Show

end;

Метод Move обращается к методам Hide и Show. Смысл этих обращений очевиден: сначала объект делается невидимым на экране (вызывается метод Hide), затем перемещается (координаты точки на экране нужным образом изменяются) и, наконец, снова делается видимым (вызывается метод Show).

Но что произойдет, если метод Move в данном виде использовать в программе с двумя объектами: Dot и Ring? Вспомним, что метод Move наследуется объектом Ring от объекта Dot, а методы Hide и Show, поскольку сокрытие и показ окружности на экране осуществляется не так, как точки, в объекте Ring предопределяются. Так вот, для точки при этом никаких осложнений не возникает. Точка без проблем «гаснет», меняет координаты и затем снова «зажигается», поскольку все вызываемые здесь методы (сначала Move, а затем из него – Hide и Show) для объекта Dot являются «родными».

Что касается окружности, то при вызове метода Ring.Move система пытается обнаружить метод с таким именем в описании объектного типа Ring и, не найдя его, продолжает поиск в объекте-предке. В результате имеет место обращение к методу Dot.Move. После этого из метода Move должны быть вызваны (переопределенные) методы Ring.Hide и Ring.Show, однако этого не происходит: из унаследованного метода Dot.Move экземпляр объекта Ring1, вместо Ring.Hide и Ring.Show, вызывает одноименные методы объекта Dot. Это объясняется тем, что методы Dot.Move, Dot.Hide и Dot.Show жестко связаны, поскольку они были откомпилированы в едином контексте – объектном типе Dot. Иными словами, связь между этими методами, которая была установлена при компиляции, имеет статический характер. В этом случае также окажется перемещена точка, а не окружность.

  • Как же сделать так, чтобы методы Hide и Show вызывались в зависимости от того, экземпляр какого объекта инициировал обращение к методу Move? Здесь на помощь приходит механизм, известный как динамическое (или позднее) связывание – в отличие от статического (или раннего) связывания. Указанный механизм реализуется с помощью виртуальных методов.

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

Вот как выглядят описания объектных типов Dot и Ring с виртуальными методами.

Dot=object

a, b : integer;

constructor Init (x, y : integer);

procedure Show; virtual;

procedure Hide; virtual;

procedure Move (Da, Db: integer);

end;

{---------------------------------------------------}

Ring = object (Dot)

Rad : integer;

constructor Init (x, y, r : integer);

procedure Show; virtual;

procedure Hide; virtual;

end;

  • (Здесь CONSTRUCTOR – это зарезервированное слово, обозначающее особый вид процедуры. Вызов виртуального метода должен быть предварен обращением к такой процедуре).

Теперь вернемся к вызову экземпляром объекта Ring1 метода Move, а из него – методов Hide и Show. Здесь сначала система обращается к методу Dot.Move (предварительно метод с таким именем ищется в описании объекта Ring, и, поскольку его здесь нет, имеет место обращение к соответствующему методу объекта-предка). После этого для перемещения окружности сначала требуется сделать ее невидимой (вызвать метод Ring.Hide), затем нужным образом изменить координаты ее центра и, наконец, отобразить окружность в новом месте на экране (вызвать метод Ring.Show). А поскольку при выполнении программы Turbo Pascal обеспечивает обращение именно к тому виртуальному методу, который определен для вызывающего объекта, из метода Dot.Move имеют место обращения к методам Ring.Hide и Ring.Show.

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

Пример

program ObjDotCirc;

uses crt, graph;

type

Dot=object

a, b: integer;

constructor Init (x, y: integer);

procedure Show; virtual;

procedure Hide; virtual;

procedure Move (Da, Db: integer)

end;

{------------------------------------------------}

Ring = object (Dot)

Rad : integer;

constructor Init (x, y, r : integer);

procedure Show; virtual;

procedure Hide; virtual

end;

{----------------------------------------------}

constructor Dot.Init;

begin

a : =x; b : =y

end;

{-----------------------------------------------}

procedure Dot.Show;

begin

PutPixel (a, b, White)

end;

{-----------------------------------------------}

procedure Dot.Hide;

begin

PutPixel (a, b, Black)

end;

{----------------------------------------------}

procedure Dot.Move;

begin

Hide;

a :=a+Da; b := Db;

Show

end;

{----------------------------------------------}

constructor Ring.Init;

begin

a :=x;

b :=y;

Rad :=r

end;

{-----------------------------------------------}

procedure Ring.Show;

begin

SetColor (White);

Circle (a, b, Rad)

end;

{----------------------------------------------}

procedure Ring.Hide;

begin

SetColor (Black);

Circle (a, b, Rad)

end;

{-----------------------------------------------}

var i, j, k, Err : integer; a : char; Dot1 : Dot; Ring1 : Ring;

begin {тело программы}

i :=detect;

InitGraph(i, j, ’’);

Err :=GraphResult;

if Err <> grOK then

Writeln (GraphErrorMsg(Err))

else

begin

Dot1.Init (GetMaxX div 2, GetMaxY div 2);

Dot1.Show;

Ring1.Init(GetMaxX div 2, GetMaxY div 2, GetMaxY div 6);

Ring1.Show;

while KeyPressed do a : =ReadKey;

repeat

begin

a : =ReadKey;

case ord(a) of

72 : Dot1.Move (0,-5);

80 : Dot1.Move (0,5);

77 : Dot1.Move (5,0);

75 : Dot1.Move (-5,0);

73 : Ring1.Move (0,-5);

81 : Ring1.Move (0,5);

79 : Ring1.Move (5,0);

71 : Ring1.Move (-5,0)

end

end;

until a = chr (27)

end

end.

Данная программа отличается от программы, приведенной в этой главе выше, только тем, что здесь дополнительно объявлен тип-потомок Ring. Кроме того, методы Show и Hide обоих объектов объявлены как виртуальные, а методы Init преобразованы в конструкторы.

Кроме того, оператор CASE в новой программе дополнен следующими вариантами.

73 : Ring1.Move (0,-5);

81 : Ring1.Move (0,5);

79 : Ring1.Move (5,0);

71 : Ring1.Move (-5,0);

Представленные варианты обеспечивают перемещение окружности по экрану в четырех направлениях. Перемещение имеет место при нажатиях клавиш <PgUp> (вверх), <PgDn> (вниз), <End> (вправо) и <Home> (влево), генерирующих скэн-коды соответственно 73, 81, 79 и 71.

  • В этой программе особое внимание следует обратить на вызовы метода Move в теле оператора CASE. В первых четырех случаях имеет место обращение к методу Dot.Move, а в остальных случаях – к методу Ring.Move. Однако мы знаем, что метод Move – единственный. Он объявлен в объекте Dot и унаследован объектом Ring. Иными словами, один и тот же метод Move работает по-разному (перемещает точку или окружность) – в зависимости от того, какой объект его вызывает. Такое свойство объектов называется полиморфизмом.

Перечень источников:

1. Ахо Альфред В., Хопкрофт Джон, Ульман Джеффри Д. Структуры данных и алгоритмы: Пер. с англ.: Уч.пос.- М.: Издательский дом “Вильямс”, 2000.-324 с.

2. Бауэр Ф.Л., Гооз Г. Информатика. Вводный курс: В 2 ч.- М.:Мир, 1990.-543с.

3. Вирт Н. Алгоритмы + структуры данных = программы.- М.: Мир, 1985. (Алгоритмы и структуры данных.- М.: Мир, 1989.) (Алгоритмы и структуры данных. – СПб.: Невский Диалект, 2001. (2-е изд., испр.))