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