![]() |
![]() |
Altair |
![]()
Сообщение
#1
|
![]() Ищущий истину ![]() ![]() ![]() ![]() ![]() ![]() Группа: Модераторы Сообщений: 4 824 Пол: Мужской Реальное имя: Олег Репутация: ![]() ![]() ![]() |
Один из подходов в программировании носит название: "Объектно-ориентированное программирование" или, сокращенно, ООП. Идея этого подхода заключается в стремлении связать данные с обрабатывающими эти данные процедурами в единое целое - ОБЪЕКТ.
В Турбо Паскале ООП реализовано начиная с версии 5.5. ООП основано на трех важных принципах, придающих объектам особые свойства и отличающих их от других типов данных. Рассмотрим их:
Сначала может показаться, что при описании нет разницы между объектами и записями. На деле разница заключается в том, что при описании объекта в него могут входить заголовки процедур и функций, ведь по определению объект это "Данные+Алгоритмы". Перефразируя Вирта, можно сказать что: "Алгоритмы+Структуры Данных=Объекты". Выглядит описание объекта так: type
T = object
{ переменные, или ПОЛЯ объекта }
...
{ заголовки процедур и функций, или МЕТОДЫ }
end;
Переменные, описанные в объекте называются полями, а процедуры их обработки - методами. В самом типе объекта описан лишь интерфейс метода (так же, как при написании модуля в разделе INTERFACE мы описываем только заголовки), т.е. способ его вызова. Сам метод описывается в разделе процедур и функций, и заголовок имеет нестандартную форму (перед его именем обязательно указывается имя типа объекта, к которому этот метод принадлежит): procedure ObjectType.ProcedureName(...);
Т.е <имя_объекта>.<имя_процедуры> Параметры описываются как обычно, за исключением того, что внутри метода всегда доступны поля объекта непосредственно по их именам. Например: type
TA = object
a: Integer;
procedure give(x:integer);
end;
{ ... }
procedure TA.give(x: integer);
begin
a := a + x;
end;
Так как задание заголовка метода в описании типа объекта является опережающим описанием, то так же, как при реализации процедур и функций в разделе IMPLEMENTATION, список параметров может опускаться, то есть, такое описание будет полностью аналогично предыдущему: procedure TA.give;
begin
a := a + x;
end;
Переменные типа "объект" можно создавать как обычно, объявлением в списке переменных (при таком способе для использования экземпляров объектов - переменных объектного типа - в программе, их вызывают так: <имя_объекта>.<имя_метода>): type TA = object ... end;
var a: TA;
begin
a.give(5);
end.
но большее распространение получил метод их размещения в динамической памяти. Для этого нужно создать дополнительный тип - указатель на объект (в таком случае для обращения к полям или методам объекта указатель надо разыменовать): type
PTA = ^TA;
TA = object ... end;
var p_a: PTA;
begin
new(p_a);
p_a^.give(5);
end.
Наследование При описании объекта-наследника (также называемого производным типом), имя его родителя указывается в скобках. Например: TA =
object
{ поля и методы объекта TA }
end;
TA1 =
object(TA) { потомок TA }
{ ... }
end;
Поля и методы родителя могут появляться в реализации методов наследника, как если бы они были описаны явно в самом наследнике. Процесс наследования является транзитивным: если TB наследуется от TA, а TC в свою очередь - наследник TB, то тип TC также считается наследником TA. Следует обратить внимание на то, что при наследовании полей в производном типе нельзя объявлять идентификаторы, которые уже использовались в одном их типов-родителей. (На методы это ограничение не распространяется, в случае повторного определения метода он считается переопределенным, и ко всем потомкам будет переходить именно переопределенный метод.) Из "умения" объектов "наследовать" вытекает правило присваивания для переменных типа "Объект": переменным такого типа можно присваивать не только значения этого же типа, но и значения любого производного типа. Например, при таком определении: type
TA = object
x: integer;
{...}
end;
TB = object(TA)
y: integer;
{...}
end;
var a: TA; b: TB;
для копирования значения X записанного в переменной b в переменную a достаточно выполнить присваивание: a := b;
Внимание: Операция присваивания возможна только таким путем: "Родитель <-- Наследник" При этом гарантируется заполнение всех полей типа "Родитель" содержимым соответствующих полей типа "Наследник", так как в "Наследнике" число полей не меньше, чем в родителе. В противном случае могла бы возникнуть неопределенность с "лишними" полями, которых нет в "Родителе". Операцией присваивания копируются только поля. Методы таким образом не присваиваются... Использование объектов Посмотрим теперь на практике, как работать с объектами. uses crt;
{ Глобальная константа }
const NN = 10;
type
{ это наш массив, на основе которого будем делать объекты }
atype = array[1 .. nn] of integer;
T1 = object
a:atype;
n:integer;
procedure Vec; { процедура ввода массива }
procedure Print; { процедура вывода массива }
end;
T2 = object(T1)
s:integer; { сумма }
procedure Print; { перекроем новым методом, посмотрим результат }
procedure Summ; { посчитаем сумму эл-тов }
end;
{ ---------- теперь опишем методы ------- }
procedure T1.Vec;
var c, i: Integer;
begin
repeat
write('Введите длинну массива n = '); readln(n)
until (N > 0) and (n <= NN);
for i := 1 to n do begin
write('a[',i,'] = '); readln(a[i])
end
end;
{ ----- }
procedure T1.Print;
var i: Integer;
begin
WriteLn;
for i := 1 to n do write(a[i]:6);
WriteLn
end;
{ ----- }
procedure T2.Print;
var F:text; fn:string; i:integer;
begin
Writeln('Введите имя файла для записи туда массива');
ReadLn(fn); Assign(f, fn); ReWrite(f);
for i := 1 to n do WriteLn(f,a[i]);
Close(f)
end;
{ ----- }
procedure T2.Summ;
var i: integer;
begin
s := 0;
for i := 1 to n do s := s + a[i];
end;
{ ----- }
var
b: T1; c: T2;
begin
{
Теперь проверим работу объектов. При вызове Т2, должны быть доступны
ВСЕ методы объекта родителя, т.е. Т1, но метод PRINT перекрывается
}
с.Vec;
b.Print;
c.Print;
c.Summ;
end.
Итак, рассмотрим эту программу. Это первая наша программа с объектами. В ней есть 2 объекта - родитель Т1 и потомок Т2. Я специально сделал так, чтобы в них были методы (процедуры) с одинаковыми именами. Обратите внимание, что мы не описывали в Т2 процедуру Vec, однако, при вызове: c.Vec
метод заработал. Это произошло, потому что объект T2 является потомком Т1, и он знает все методы и данные своего родителя Т1. И наоборот Т1 не знает, что есть процедура Summ. На этом примере мы также убедились, что при перекрытии работает тот метод, который был описан позднее (т.е. метод потомка, а не родителя). То есть при вызове C.Print пошла запись в файл, а не на экран монитора, как есть в методе родителя. Таким образом, при наследовании объект-потомок наследует все методы родителя и перекрывает одноименные. |
![]() ![]() |
![]() |
Текстовая версия | 24.07.2025 17:26 |