2.14. Пример на правила видимости и передачи параметров
unit factorials;
uses. . . ;
var
k, n,m: integer; {глобальные целые переменные}
p:extended;
ch: char; {глобальная литерная переменная}
function fact1(n:integer): extended;{процедура-функция, передача
входного параметра n по значению}
var i: integer;
begin
if n=0 then n:=1;
result:=1;
for i:=1 to n do result:=result*i;
end;
procedure fact2;{процедура, передача входного параметра через
глобальную переменную n, возврат результата через
глобальную переменную p}
var i: integer;
result:extended;
begin
if n=0 then n:=1;
result:=1;
for i:=1 to n do result:=result*i;
p:=result;
end;
procedure fact3(n:integer; var r:extended);{процедура, передача
входного параметра n по значению,
возврат результата через параметр r по имени}
var i: integer;
result:extended;
begin
if n=0 then n:=1;
result:=1;
for i:=1 to n do result:=result*i;
r:=result;
end;
function fact4(n:integer):extended; {процедура-функция, передача
входного параметра n по значению, побочный
эффект – изменение глобальной переменной m}
var
i: integer;
begin
if n=0 then n:=1;
result:=1;
for i:=1 to n do result:=result*i;
fact4 =result;
m:=m+1;{побочный эффект}
end;
begin {тело основной программы}
k:=…;
m:= 0;n:=…;p=…;
case ch of
'1' : p := fact1( n );
'2' : fact2 ;
'3' : fact3( n, p );
'4' : p := fact4( n );
end;
……
End.
2.15. Рекурсия
Очень часто в математических выражениях используется так называемое рекурсивное определение функций:
factorial(1)=1; для n>1: factorial(n)= factorial(n-1)*n.
Многие алгоритмы задаются рекурсивно. Например, анализ транслятором вложенных процедур. Для реализации такого рода алгоритмов используется рекурсивный вызов процедур. Например, вычисление факториала можно задать как
function factorial(n:integer):extended;
begin
result:=1;
if n>=1 then result:=n*factorial(n-1)
end;
У рекурсии есть недостатки: а) при каждом вложенном вызове создается копия локальных переменных процедуры, они храняться в стеке. При большой глубине вложенности стек может переполниться; b) очень часто встречается ситуация бесконечной рекурсии. Так, если бы мы не написали условия n>=1, рекурсные вызовы продолжались бы вплоть до переполнения:
function factsfoo(n:integer):extended;
begin
result:>=n*factorial(n-1)
end;
Основные принципы объектно-ориентированного программирования (ООП) на примере языка Object Pascal (Delphi) со статической версией объектной модели
Рассматриваемая версия объектной модели характерна для языка TurboPASCAL (TP5.5 – TP7.0) и очень близка к объектной модели C++ ( за исключением того, что в C++ поддерживается также множественное наследование и ряд других дополнительных возможностей). В Delphi эта модель поддерживается, но рассматривается как устаревшая. Основной считается модель с динамически создаваемыми объектами, во многом близкая по идеологии к объекной модели языка Java.
2.1. Инкапсуляция. Объект. Поля данных и методы объекта
Инкапсуляцией называется объединение каких-либо относительно независимых элементов в единое целое. В стандартном PASCAL существует тип record — "Запись":
type
tLocation=
record {поля записи}
X, Y:Integer
end;
var Location1,Location2:tLocation;
a:integer;
Поля записи — самостоятельные переменные. Доступ к ним — через квалификацию с помощью точки (составное имя с разделительной точкой). К полям можно обращаться по отдельности. Например, присвоить переменной a значение поля X:
a:=Location1.X
Либо изменить значение поля Y:
Location1.Y:=212
А можно действовать с инкапсулированными в запись полями как с единым целым:
Location2:=Location1
при этом полю X записи Location1 будет присвоено значение X записи Location2, и, аналогично, полю Y в Location1 — значение Y записи Location2.
Развитием идеи объединения (инкапсуляции) различных полей стали объекты.
Пример:
объект:
точка на экране tDot;
ее данные:
координаты X и Y — целые переменные;
светится или нет на экране active — булевская переменная;
ее методы действия:
"нарисовать": Show;
"скрыть": Hide;
"передвинуть по X и Y на величины dX и dY": Move(dX, dY)
"передвинуть в точку с координатами X и Y": MoveTo(X, Y)
Использование в программе:
var aDot:tDot;
...
aDot. X:=10; {координате X точки Dot присвоить значение 10}
aDot. Y:=20;
aDot. Show; {вызвать метод показа точки Show; говорят: "точка себя рисует"}
Описание методов:
procedure tDot. Show;
begin
… {рисование точки текущим цветом пера}
active:=true;
end;
procedure tDot. Hide;
begin
… {рисование точки цветом фона}
active:=false;
end;
procedure tDot. Move(dX, dY:Integer);
begin
Hide;
X:=X+dX;
Y:=Y+dY;
Show;
end;
3.2 Задание модуль класса (статическая объектная модель)
Теперь мы можем наметить, как будет выглядеть модуль с нашим классом:
Unit myFigure;
uses …;
interface
type
tDot=
object
X, Y:Integer;
active:Boolean;
procedure Show;
procedure Hide;
procedure MoveBy(dX, dY:Integer); {сдвиг на dX, dY}
procedure MoveTo(NewX, NewY:integer);{рисование в точке NewX, NewY}
end;
tCircle=
object(tDot)
...
end;
implemetation
{——— раздел реализации ———}
procedure tDot. Show;
begin
...
end;
procedure tDot. Hide;
begin
...
end;
procedure tDot. MoveBy(dX, dY:Integer);
begin
...
end;
procedure tDot. moveTo(X,_,Y_:integer);
begin
…
end;
begin
{раздел инициации. Всегда делать пустым!!!}
end.
Модули. Секции декларации, интерфейса и реализации. Области видимости.
На самом деле общая структура модуля при наличии в нем класса несколько сложнее. Обсудим ее более подробно.
Написание собственного модуля (unit) начинается с указания имени модуля. В нашем случае он называется myFigure. Он должен храниться в файле с тем же именем, но с расширением. pas (файл языка PASCAL), т. е. myFigure. pas.
unit myFigure;
секция декларации
interface
uses... , m1,…; {наш модуль кроме списка модулей, автоматически созданного Delphi, использует модули m1 и т. Д.}
type
tDot=
object
active:Boolean;
X, Y:integer;
procedure Show;
procedure Hide;
procedure MoveBy(dX, dY:Integer);
procedure MoveTo(NewX_,NewY_:Integer);
procedure Init(X_,Y_,:Integer);{процедура инициализации объекта}
{если в объекте есть поля X и Y, то эти имена нельзя использовать в
качестве формальных параметров. Выход из этой ситуации — добавлять
символ _ после имени, чтобы было понятно, к использованию какого поля
объекта относится передаваемый параметр}
end;
tCircle=
object(tDot)
...
end;
procedure... {описание процедур и функций, видимых в других модулях}
var... {описание глобальных переменных, видимых в других модулях}
секция реализации
implementation
uses m2,m3; {модули m2 и m3 доступны процедурам и функциям области
реализации}
var...; {описание глобальных переменных, доступных только в данном модуле}
описание реализации методов объектов
procedure tDot. Show;
begin
...
end;
procedure tDot. Hide;
begin
...
end;
procedure tDot. Move(dX, dY:Integer);
begin
...
end;
procedure tDot. MoveTo(NewX, NewY:Integer);
begin
...
end;
procedure tDot. Init(X_,Y_:Integer);
begin
X:=X_;
Y:=Y_;
tDot. Hide;
end;
procedure tCircle...;
begin
...
end;
...
begin {секция инициализации — обычно пустая}
end.
Декларация использования других модулей в области интерфейса аналогична вставлению соответствующих модулей (а на самом деле ссылки на модуль) вместо этой декларации. Поэтому в интерфейсе модуля m1 нельзя писать uses myFigure, т. к. тогда получилось бы, что в модуль myFigure вложен модуль m1, который, с свою очередь, вложен в модуль myFigure, т. е. myFigure вложен сам в себя. Для раздела реализации подобных ограничений нет, и в модулях m2 и m3 может стоять ссылка uses myFigure. В интерфейсе всегда надо ссылаться на модули, в которых описаны классы-прародители, а также на те, из которых надо брать определенные в них типы для использования в интерфейсе своего модуля.
Классы, экземпляры класса, наследование, иерархия.
Объектный тип (например, tDot) называется классом. Переменная данного типа — объект или, что то же самое, экземпляр класса. То есть при задании
Var dDot:tDot
переменная dDot – это объект.
tDot — тип-прародитель (класс-прародитель),
tCircle=object(tDot) {тип-наследник (класс-наследник)};
...
end;
tRectangle=object(tDot) {другой наследник};
...
end;
Тут tCircle и tRectangle — прямые потомки tDot (т. е. он их прямой прародитель) и т. п.
Далее можно определить другие классы-наследники:
tTriangle=object(tDot)
...
end;
tFilledTriangle=object(tTriangle)
...
end;
tFilledCircle=object(tCircle)
...
end;
tRedCirlce=object(tCirlce)
...
end;
tGreenCirlce=object(tCirlce)
...
end;
tFilledRectangle=object(tRectangle)
...
end;
Иерархия этих классов:
Суть наследования заключается в том, что потомки могут использовать поля данных и методы прародителей без определения этих полей и методов в классе потомка. Говорят, что поля данных и методы прародителей наследуются потомками.
Пример:
type
tDot=
object
active:Boolean;
X, Y:Integer;
procedure Show;
procedure Hide;
procedure MoveBy(dX, dY:Integer);
procedure MoveTo(NewX, NewY:Integer);
procedure Init(X_,Y_:Integer);
end;
tCircle=
object(tDot)
radius:Real;
procedure Show;
procedure Hide;
procedure MoveBy(dX, dY:Integer);
procedure MoveTo(NewX, NewY:Integer);
procedure Init(X_,Y_,R_:Integer);
end;
var aDot: tDot;
aCircle: tCircle;
В теле объекта aCircle можно пользоваться полями X, Y, active, как если бы они были определены в tCircle, а не в tDot. При этом они никакого отношения не имеют к объекту aDot, т. е. к tDot. X, aDot. Y, aDot. Active. Это aCircle. X, aCircle. Y, aCircle. Active. Но по своей структуре они дублируют соответствующие поля объекта aDot.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |


