Раннее и позднее связывание методов
I. Основные принципы ООП
1. Инкапсуляция– принцип ООП, который заключается в объединении в единое целое данных и алгоритмов их обработки.
Данные объектав ООП называются полями объекта, а алгоритмы, т. е. действия над данными объекта,называются методами объекта, которые оформляются в виде подпрограмм.
2. Наследование– принцип ООП, который заключается в свойстве объектов порождать своих потомков.
Объект-потомок автоматически наследует от родителя все поля и методы, может дополнять объекты новыми полями и заменять или дополнять методы родителя.
3. Полиморфизм – это свойство объектов, имеющих одного общего родителя, решать разными способами схожие по смыслу задачи.
Полиморфизм методов выражается в наличии у предка и потомка методов с одинаковыми именами, имеющих разную реализацию.
II. Структура объявления объектного типа
TYPE
<имя_типа>=OBJECT
<имя_поля>:<тип_поля>;
…
<методы>;
END;
После определения типа объекта должны следовать описания всех методов, перечисленных в объектном типе, которые представляют собой тексты процедур и функций. Отличие от обычного описания заключается в том, что имя подпрограммы, являющейся методом объекта, указываемое в её заголовке, состоит из 2 частей:
<имя_типа>.<имя_подпрограммы>
III. Свойство наследования
Каждый новый объектный тип может строиться на основе ранее определенного типа, называемого родительским типом, от которого наследуются все поля и методы. Для того, чтобы произошло наследование, в определение объектного типа после OBJECT в скобках необходимо указать имя родительского типа.
Правила наследования.
1. В определении типа наследника не должно быть полей, совпадающих по именам с полями родителя; имена методов у наследников и родителя могут совпадать – в этом проявляется свойство полиморфизма.
2. При построении объектного типа наследника, в первую очередь, наследуются поля родителя, затем добавляются поля наследника; после полей наследуются все методы родителя, затем добавляются методы наследника, если их имена не совпадают с именами методов родителя; если совпадения существуют, то это означает, что методы наследника являются полиморфными методами и замещают одноименные методы предка.
Свойство полиморфизма.
Свойство полиморфизма заключается в том, что в определении типа наследника могут быть методы, одноименные с именами методов родителя.
Правила наследования при полиморфизме:
· одноименные методы наследника заменяют методы родителя;
· методы наследника, не совпадающие по именам с методами родителя, добавляются после методов родителя.
IV. Виртуальные методы
Раннее и позднее связывание методов.
Методы объекта являются статическими методами, если компилятор размещает их и разрешает все ссылки на них во время компиляции и компоновки. Процесс, с помощью которого вызовы статических методов однозначно разрешаются компилятором во время компиляции, называется ранним связыванием.
При позднем связываниивызывающий и вызываемый методы не могут быть связаны по адресам во время компиляции, поэтому включается механизм, позволяющий выполнить связывание в тот момент, когда вызов действительно произойдет, т. е. во время выполнения программы.
Виртуальные методы и таблица виртуальных методов (ТВМ).
Позднее связывание реализуется с помощью виртуальных методов, признаком которых является наличие ключевого слова VIRTUAL после заголовка метода в определении объектного типа.
Конструктор.
Во время выполнения программы до исполнения виртуальных методов любого объекта должен выполниться метод-конструктор для данного экземпляра объекта, в который компилятор вставляет действия по связи неразрешенных ссылок с ТВМ.
Конструктор– это специальный вид процедуры, которая выполняет установочную работу для обеспечения позднего связывания. Все типы объектов, имеющих виртуальные методы, должны содержать конструктор, который всегда вызывается до первого вызова виртуального метода. Вызов виртуального метода без предшествующего обращения к конструктору может вызвать блокировку системы.
Конструктором может быть только процедура, в заголовке которой вместо служебного слова PROCEDURE необходимо написать CONSTRUCTOR.
Конструктор не может быть виртуальным.
После выполнения метода-конструктора при вызове виртуальных методов их адреса определяются из ТВМ.
Деструктор.
Деструктор служит для очистки памяти и удаления динамически размещенного объекта. Обычно в деструкторе указываются завершающие действия с объектом. В заголовке деструктора служебное слово PROCEDURE заменяется на DESTRUCTOR Для одного и того же типа объекта можно определить несколько деструкторов. Деструкторы можно наследовать, они могут быть статическими или виртуальными.
V. Пример объектного типа
1. Постановка задачи: реализовать движение точки на экране.
2. Математическая модель: каждая точка на экране характеризуется координатами x, y и состоянием v –видима/невидима.
3. Объявление объектного типа точка:
type
POINT=object
X, Y: integer; {координаты}
V: boolean; {признак видимости: TRUE-видима; FALSE-невидима}
function GET_X: integer; { получение координаты Х }
function GET_Y: integer; { получение координаты Y }
function GET_V: boolean; { получение признака видимости }
procedure INIT(X0, Y0: integer); { задание координат }
procedure TURN_ON; { рисование точки – получение видимой точки }
procedure TURN_OFF; { стирание точки – получение невидимой точки}
procedure MOVE(XN, YN: integer); { перемещение точки }
end;
4. Определение методов объекта POINT:
function POINT.GET_X; { получение координаты Х }
begin
GET_X:=X;
end;
function POINT.GET_Y; { получение координаты Y }
begin
GET_Y:=Y;
end;
function POINT.GET_V; { получение признака видимости }
begin
GET_V:=V;
end;
procedure POINT.INIT; { задание координат }
begin
X:=X0;
Y:=Y0;
V:=false;
end;
procedure POINT.TURN_ON; { получение видимой точки }
begin
if not V then
begin
PutPixel(X,Y,GetColor); { GetColor возвращает текущий цвет }
V:=true;
end;
end;
procedure POINT.TURN_OFF; { получение невидимой точки – стирание точки }
begin
if V then
begin
PutPixel(X,Y,GetBkColor); { GetBkColor возвращает фоновый цвет }
V:=false;
end;
end;
procedure POINT.MOVE; { перемещение точки }
var
F: boolean;
begin
F:=V;
if F then
TURN_OFF;
X:=XN;
Y:=YN;
if F then
TURN_ON;
end;
Свойство наследования.
Кроме точки, можно задать другие объекты, описывающие геометрические фигуры, например, объект типа окружность, который определяется радиусом, координатой центра, признаком видимости, и с ним возможны такие же действия, как с точкой: получение видимой окружности; получение невидимой окружности; перемещение окружности (изменение координат центра). Чтобы этот объект привязать к координатной сетке экрана, необходимо определить объектный тип PLACE (место), который будет иметь данные, общие для всех геометрических фигур, – координаты привязки объекта к экрану X и Y:
type
PLACE=object
X, Y: integer;
procedure INIT(X0, Y0: integer);
function GET_X: integer;
function GET_Y: integer;
end;
{ далее следует описание методов }
Каждый новый объектный тип может строиться на основе ранее определенного типа, называемого родительским типом, от которого наследуются все поля и методы. Чтобы произошло наследование, в объявлении объектного типа после OBJECT в скобках указывается имя родительского типа.
Используя тип PLACE как родительский, можно определить объектный тип POINT таким образом:
type
POINT=object(PLACE)
V: boolean;
procedure INIT(X0, Y0: integer);
function GET_V:boolean;
procedure TURN_ON;
procedure TURN_OFF;
procedure MOVE(XN, YN: integer);
end;
{ далее следует описание методов }
6. Свойство полиморфизма заключается в том, что в объявлении типа наследника могут быть методы, одноименные с именами методов родителя.
Объявление типа окружность c использованием в качестве родителя типа POINT будет следующим:
type
CIRCL=object(POINT)
R: integer;
procedure INIT(X0, Y0, R0: integer); { задание окружности }
procedure TURN_ON; { получение видимой окружности }
procedure TURN_OFF; { получение невидимой окружности }
procedure MOVE(XN, YN: integer); { перемещение окружности}
function GET_R: integer; { получение радиуса }
end;
Определение методов с использованием наследования при полиморфизме:
procedure CIRCL.INIT;
begin
X:=X0; Y:=Y0;
R:=R0;
V:=false;
end;
procedure CIRCL.TURN_ON;
begin
V:=true;
CIRCLE(X,Y,R);
end;
procedure CIRCL.TURN_OFF;
var
C: byte;
begin
C:=GetColor;
SetColor(GetBkColor); { установка цвета рисования }
Circle(X,Y,R);
V:=false;
SetColor(C);
end;
procedure CIRCL.MOVE;
var
F: boolean;
begin
F:=V;
if F then
TURN_OFF;
X:=XN;
Y:=YN;
if F then
TURN_ON;
end;
function CIRCL.GET_R;
begin
GET_R:=R;
end;
В результате текст процедуры CIRCL.MOVE совпадает с текстом процедуры POINT.MOVE, но машинные коды у этих процедур будут разные; CIRCL.MOVE при своем исполнении обращается к адресам процедур CIRCL.TURN_ON и CIRCL.TURN_OFF, а процедура POINT.MOVE – к адресам процедур POINT.TURN_ON и POINT.TURN_OFF.
Раннее и позднее связывание методов.
Ранее рассмотренные статические методы компилятор размещает и разрешает все ссылки на них во время компиляции и компоновки (раннее связывание).
Для примера, описанного ранее, в объекте CIRCL можно наследовать метод MOVE у объекта POINT. Методы TURN_ON и TURN_OFF должны быть объявлены виртуальными, чтобы с ними произошло позднее связывание.
Компилятор оставляет после компиляции неразрешенными ссылки к тем методам, которые объявлены виртуальными. Для описанного ранее примера в методе MOVE неразрешенными ссылками будут адреса методов TURN_ON, TURN_OFF, т. е. процедура MOVE будет не готова к исполнению после компиляции, т. к. ее машинный код полностью не определен.
Во время выполнения программы до исполнения виртуальных методов любого объекта должен выполниться метод-конструктор для данного экземпляра объекта, в который компилятор вставляет действия по связи неразрешенных ссылок с ТВМ.
Объявления объектных типов PLACE, POINT и CIRCL с использованием конструктора и виртуальных методов:
type
PLACE=object
X,Y: integer;
constructor INIT(X0, Y0: integer);
function GET_X: integer;
function GET_Y: integer;
end;
POINT=object(PLACE)
V: boolean;
constructor INIT(X0, Y0: integer);
function GET_V: boolean;
procedure TURN_ON; virtual;
procedure TURN_OFF;virtual;
procedure MOVE(XN, YN: integer);
end;
CIRCL=object(POINT)
R: integer;
constructor INIT(X0, Y0, R0: integer);
function GET_R: integer;
procedure TURN_ON; virtual;
procedure TURN_OFF; virtual;
end;
Деструктор необходим только для динамически размещенных объектов, так как он определяют фактический размер объекта для корректного освобождения памяти от динамического объекта, причем эти действия выполняются деструктором по умолчанию в дополнение к действиям, описанным в теле деструктора.
Обращение к деструктору, как правило, указывается при освобождении памяти от динамического объекта при вызове процедуры DISPOSE:
DISPOSE (<имя_объекта>, <вызов_деструктора>);
Метод деструктора также может быть пустым:
DESTRUCTOR <ИМЯ>;
BEGIN
END;
При этом компилятором генерируетсякод в ответ на зарезервированное слово DESTRUCTOR.