Правила присваивания. Совместимость типов для объектов.

Для потомков при использовании их "на месте" прародителей любого уровня имеется два основных варианта совместимости типов (по вызовам и по присваиваниям) между:

1. экземплярами объектов,

2. указателями на экземпляры объектов,

3. формальными и фактическими параметрами.

Потомки могут использоваться вместо прародителей (но не наоборот!) по следующей причине: используемый тип должен для совместимости полностью "заполнять" поля типа, вместо которого он подставляется.

Например:

aDot:=aCircle;

aDot:=aArc;

{aCircle:=aDot - так нельзя!}

При этом в наш объект, которому присваивается значение, копируются только те поля данных, которые имеются в Dot, а остальные оказываются ненужными и игнорируются. Если бы мы попытались сделать присваивания в обратную сторону (aCircle:=aDot или aArc:=aDot), часть полей результата не смогла бы заполниться. Поэтому присваивания типа "потомок:=прародитель" недопустимы. Стоит отметить, что описанные выше разрешенные присваивания объектов как таковых в последующих версиях PASCAL невозможны, и работа с объектами идет только через указатели (смотри далее раздел про Delphi).

Для указателей присваивание pDot:=pCircle допустимо, а pCircle:=pDot недопустимо, т.е. указателю на прародителя можно присвоить указатель на потомка, но не наоборот. Это правило логически вытекает из описанного выше правила совместимости для объектов. Оно сохранено в Delphi, хотя его основа — правило присваивания объектов — в Delphi ликвидировано. Поэтому изучение объектной модели Turbo Pascal полезно для правильного понимания объектной модели Delphi.

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

Особенностью рассмотренных правил совместимости является то, что при работе с указателями на полиморфные объекты (т.е. при наличии в объектах виртуальных методов) бывает заранее неизвестно, какой тип вызовется. Вызов:

pDot^.Show

может показать и точку, и круг, и дугу (и даже еще неизвестного нам потомка) в зависимости от того, указатель на какую фигуру был присвоен указателю pDot. Это может зависеть от различных условий. Например, от решения пользователя в программе. В приводимом ниже примере указателю типа tpDot намеренно дано название не pDot, а pFigure, чтобы подчеркнуть, что он может указывать на произвольную фигуру — потомок tDot:

var FigKey:Char;

pFigure:tpDot;

begin

...

outTextXY('Choose type of a figure:0-dot,1-circle,2-arc',0,10);

readln(FigKey);

case FigKey of

'0':pFigure:=pDot;

'1':pFigure:=pCircle;

'2':pFigure:=pArc;

end;

...

pFigure^.Show; {не забыть, что до этого экземпляр должен был быть

инициализирован конструктором!}

...

end;

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