Из описания видно, что метод move обращается к методам, для того, чтобы сначала сделать объект невидимым (вызов метода hide), затем изменить его координаты, а затем вновь отобразить объект на экране (вызов метода show). В случае, когда данный метод используется в отношении объекта dot, то все работает нормально (точка при нажатии соответствующих кла-виш гаснет, и затем зажигается в новом месте), т.к. методы hide и show являются для данного объекта «родными». Если же попытаться вызвать метод move для объекта-окружности (circl.move), то система попытается найти метод с таким именем в описании объектного типа circl, и не найдя его, продолжит поиск в описании типа-предка. В результате произойдет обращение к методу dot.move. После этого из метода circl.move должны быть вызваны переопределенные методы circl.hide и circl.show. Однако этого не происходит: из унаследованного метода do.move экземпляр объекта obj2 вызывает методы объекта-предка dot.hide и dot.show. Это объясняется тем, что методы dot.hide, dot.move и dot.show жестко связаны, т.к. были откомпилированы в едином контексте – объектном типе dot, и связь между этими методами, установленная во время компиляции носит статический характер. В этом случае будет перемещена точка, а не окружность.
Избежать подобных ситуаций позволяет механизм, известный как динамическое (или позднее) связывание – в отличие от статического (или раннего) связывания. Указанный механизм реализуется через использование так называемых виртуальных методов. Описание объектов, содержащих виртуальные методы, имеет некоторые особенности. Заголовок виртуального метода в описании объектного типа дополняется зарезервированным словом VIRTUAL. Если в потомках этого объектного типа имеются переопределенные методы (т.е. методы с тем же именем), они также должны быть объявлены как виртуальные и при этом иметь тот же набор формальных параметров, что и метод объекта-предка. Кроме того, в объектном типе, имею-щем хотя бы один виртуальный метод, должен быть и специальный метод – конструктор, который обязательно должен быть применен к экземпляру объекта до первого обращения к виртуальному методу. Конструктор обычно представляет собой метод, задающий для объекта некоторые начальные значения (т.е. выполняет его инициализацию). Конструктор может быть либо определен в данном объекте, либо унаследован от объекта-предка. При этом сам конструктор виртльным быть не может.
Вот как будет выглядеть описание объектных типов dot и circl с виртуальными методами.
type dot = object
x,y: integer;
constructor Init(a,b: integer);
procedure show; virtual;
procedure hide; virtual;
procedure move(Dx,Dy:integer);
end;
circl= object (dot)
r:integer;
constructor Init(a,b,rad: integer);
procedure show; virtual;
procedure hide; virtual;
end;
constructor dot.Init;
begin
x:=a;
y:=b;
end;
constructor circl.Init;
begin
x:=a;
y:=b;
r:=rad;
end;
procedure dot.show;
begin
PutPixel(x,y,white);
end;
procedure circl.show;
begin
setcolor(yellow);
circle(x,y,r);
end;
procedure dot.Hide;
begin
PutPixel(x,y,black);
end;
procedure circl.hide;
begin
setcolor(black);
circle(x,y,r);
end;
procedure Dot.move;
begin
hide;
x:=x+Dx; y:=y+Dy;
show;
end;
Теперь при вызове метода move экземпляром obj2 система обращается к методу dot.move, унаследованному от предка. В этом методе сначала происходит обращение к методу hide (делает объект невидимым), затем - изменение координат центра окружности, и, наконец, - к методу show. А поскольку методы hide и show объявлены как виртуальные, то Turbo Pascal обеспечива-ет обращение именно к тому виртуальному методу, который определен для вызывающего объекта, т.е. будут вызваны методы circl.hide и circl.show.
Ниже представлен полный текст программы, которая рисует и перемещает точку или окружность (в зависимости от выбора пользователя). В этой программе следует обратить внимание на вызовы метода move в теле программы. Этот метод вызывается как для объекта-точки (dot.move), так и для объекта-окружности (circl.move). Однако метод move – единственный. Он объявлен в объекте dot и унаследован объектом circl. Другими словами, один и тот же метод move работает по-разному (перемещает точку или окружность) – в зависимости от того, какой объект его вызывает. Такое свойство называется полиморфизм.
program ex_obj3;
uses crt,graph;
type dot = object
x,y: integer;
constructor Init(a,b: integer);
procedure show; virtual;
procedure hide; virtual;
procedure move(Dx,Dy:integer);
end;
circl= object (dot)
r:integer;
constructor Init(a,b,rad: integer);
procedure show; virtual;
procedure hide; virtual;
end;
constructor dot.Init;
begin
x:=a;
y:=b;
end;
constructor circl.Init;
begin
x:=a;
y:=b;
r:=rad;
end;
procedure dot.show;
begin
PutPixel(x,y,white);
end;
procedure circl.show;
begin
setcolor(yellow);
circle(x,y,r);
end;
procedure dot.Hide;
begin
PutPixel(x,y,black);
end;
procedure circl.hide;
begin
setcolor(black);
circle(x,y,r);
end;
procedure Dot.move;
begin
hide;
x:=x+Dx; y:=y+Dy;
show;
end;
Var i,j,err:integer;
c:char;
dot1:dot;
obj2:circl;
begin
i:=detect;
InitGraph(i,j,'');
err:=GraphResult;
if err<>0 then
writeln(graphErrorMsg(err))
else
begin
repeat
setcolor(yellow);
OutTextXY(getMaxX div 4,GetMaxY-30,'press 1 for draw pixel, 2 for draw cirlce, ESC- exit');
c:=readkey;
case c of {выбор типа графического объекта}
{1} #49: {точка}
with dot1 do
begin
init(100,100);
show;
repeat
c:=readkey;
case c of
#0:
begin
c:=readkey;
case c of
#72: move(0,-5);
#80: move(0,5);
#77: move(5,0);
#75: move(-5,0);
end;
end;
#27: hide;
end; {case}
until (c=#27) or (c=#13);
end;
{2} #50: {окружность}
begin
obj2.init(100,100,30);
obj2.show;
repeat
c:=readkey;
case c of
#0:
begin
c:=readkey;
case c of
#72: obj2.move(0,-5);
#80: obj2.move(0,5);
#77: obj2.move(5,0);
#75: obj2.move(-5,0);
end;
end;
#27: obj2.hide;
end;
until (c=#27) or (c=#13);
end;
{ESC}#27: halt;
end;
until false;
end;
end.
|