Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Краткий конспект Лекции.doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
476.67 Кб
Скачать

4.2 Объекты, наследующие записи. Использование Типа Object

Не менее важным является и тот факт, что объекты могут наследовать характеристики и поведение того, что называется порождающие, родительские объекты (или предки).

В терминах Паскаля, объект во многом схож с записью, которая является оболочкой для объединения нескольких связанных элементов под одним именем. Предположим, нужно разработать программу вывода платежной ведомости, печатающую отчет и показывающую, сколько нужно выплатить каждому служащему за рабочий день. Запись можно организовать следующим образом:

TEmployee = record

Name: string[25];

Title: string[25];

Rate: Real;

end;

Здесь TEmployee является типом записи, т.е. шаблоном, используемым компилятором для создания переменных типа запись. Переменная типа TEmployee является экземпляром этого типа. Можно рассматривать поля Name, Title и Rate по отдельности, а когда о полях, как о работающих одновременно для описания конкретного рабочего, можно рассматривать их совокупность, как TEmployee.

Предположим, что на некоторой фирме работает несколько типов рабочих. Одни из них имеют почасовую оплату, другие - оклад, третьи - тарифную ставку и так далее. Программа должна учитывать все эти типы. Можно создать другой тип записи для каждого типа рабочего. Например, для получения данных о том, сколько должен получить рабочий с почасовой оплатой, нужно знать, сколько часов он отработал. Можно построить запись THourly вида:

THourly = record

Name: string[25];

Title: string[25];

Rate: Real;

end;

Можно также оказаться несколько догадливее и сохранить тип TEmployee путем создания поля типа TEmployee внутри типа THourly:

THourly = record

Worker: TEmployee;

Time: integer;

end;

Запись для работника-почасовика hourly должна иметь все записи, которые имеются в записи employee. Tип THourly является дочерним типом для типа TEmployee. THourly наследует все, что принадлежит TEmployee, и, кроме того, имеет кое-что новое, что делает THourly уникальным.

Этот процесс, с помощью которого один тип наследует характеристики другого типа, называется наследованием. Наследник называется порожденным (дочерним) типом, а тип, которому наследует дочерний тип, называется порождающим (родительским) типом. Ранее известные типы записей Паскаля не могут наследовать. Однако Borland Pascal расширяет язык Паскаля для поддержки наследования. Одним из этих расширений является новая категория структуры данных, связанная с записями, но значительно более мощная. Типы данных в этой новой категории определяются с помощью нового зарезервированного слова class. Тип объекта может быть определен как полный, самостоятельный тип в манере описания записей Паскаля, но он может определяться и как потомок существующего типа объекта путем помещения порождающего (родительского) типа в скобки после зарезервированного слова class.

В приводимом здесь примере платежной ведомости два связанных типа объектов могли бы определяться следующим образом:

type

TEmployee = class

Name: string[25];

Title: string[25];

Rate : Real;

end;

THourly = class(TEmployee)

Time : Integer;

end;

Обратим внимание, что использование круглых скобок означает наследование.

Большая часть конструирования объектно-ориентированных прикладных программ состоит в построении иерархии объектов, являющейся отражением генеалогического дерева объектов в приложениях.

Все возможные типы, наследующие тип TEmployee, называются дочерними типами типа TEmployee, тогда как THourly является непосредственным дочерним типом типа TEmployee. Наоборот, TEmployee является непосредственным родителем типа THourly. Тип объекта (в точности как подкаталог в DOS) может иметь любое число непосредственных дочерних типов, но в то же время только одного непосредственного родителя.

Динамическая память.

1 Карта памяти – Куча. Виды указателей

Память – последовательность байтов пронумерованных от 0.

Номер соответствует адресу. С адресов кратных 16 начинаются сегменты – участки памяти размеров не более 65 536 байт, используемые для кода программы и данных.

Участок размером в 16 байт оказывают параграфом.

Потенциальный максимум размера адресной памяти равен 65536 * 16 байт, так как сегмент задаются номером начального параграфа, а это число типа Word.

Абсолютный адрес (Адрес) состоит из Сегмента (номера параграфа) (ВА) и Смещение – номера байта в пределах сегменте (BS)

(BA:BS)

Например : Var Number : Integer; т.е.даем указание компилятору на резервирование области памяти, на которую мы будем ссылаться по имени Number адрес по котором расположена переменная Number в памяти можно определить с помощью оператора @

P1:@Number; или через функцию P1:=@Number

Размер сегмента стека – 16384 байта. Увеличить его (вплоть до 64К) можно директивой ($M) см. дальше Карту памяти или с помощью элемента Memory подменю Compile.

Возьмем пример схемы 1 Взаимодействия модулей и основной программы: Uses M1,M2

Чем меньше памяти займут модули тем больше её остаётся для размешения данных.

Статические данные – это та часть памяти которая распределяется транслятором. И в основной программе, и в блоках можно запросить динамически распределенную память (8 pn) под те или иные структуры, глобального ли локального характера.

ДРП – это вся свободная память, не занятая системными программами и сегментами называются также КУЧА.

По умолчанию минимальный размер кучи – 0, max – 655 360.

Директиву $M<Размер стека>,<Минимум кучи>,<Максимум кучи> можно запретить уменьшение ДРП до 0 и задать другой максимум.

Сам TP занимает в памяти свыше 300К. Если нужно увеличить кучу выходных из TP перед запуском EXE – файла (перед этим надо получить EXE- файл при значении Disk опция Destination в подменю Compile)

Старшие адреса

До 64 К

До 64 К

До 64 К

До 64 К

До 64 К

256 байт

Системные программы

Указатели

HeapEnd

HeapPtr

HeapOrg

OvrHeapEnd

OvrHeapOrg

Sseg:Sptr

Sseg:0000

Младшие

адреса памяти.

Ещё не распределенная память.

_

Динамически распределяемая об-ласть памяти; расширяется в сто-рону увеличения адресов

Оверлейный буфер (создаётся только в оверлейных программах)

Сегмент стека; стек прирастает в сторону уменьшения адресов.

Свободная память стека.

Сегмент данных основной программы; глобальные переменные и типизированные константы.

Сегмент кода модуля System.

Сегмент кода модуля М1

Сегмент кода модуля М2

Сегмент кода основной программы.

Префикс основной программы.

Турбо Паскаль

Системные программы

Рисунок 21- Карта памяти

Указателем называют переменную значением которой является Адрес.

На карте памяти обозначены указатели:

Начала Кучи Heap Org

оверлейного буфера OvrHeapOrg,

края занятой части кучи HeapPtr,

конца кучи HeapEnd,

Составляющие адреса Sseg и Sptr границы свободной части сегмента стека.

В начале работы программы это Сегмент кода программы (Сегмент данных) в смысле состоящей адреса.

Указатели не могут быть не введены, ни выведены.

Функция Seg(P^), Ofs(P^) возвращает составляющие Адреса, состоящие указателю P.

Знак ^ надо читать как «указывает».

Получается смысл “Адрес того места памяти, куда указывает P”.

Составляющие адреса можно вводить и выводить.

4.2 Работа с динамическими структурами данных, распределение памяти

4.2.1 Особенности объявления данных динамической структуры.

Динамическая переменная не указывается явно в описаниях переменных и у нее нет имени. Для доступа к динамическим переменным предназначены переменные специального типа данных, Называемого ссылочным типом.

Указатели – разновидности данных, ссылочного типа.

В результате указатель будет обычной переменной, а переменная на которую он будет указывать динамической.

Var P: ^Char

P:*^Char P^:Char

адрес

‘*’

Begin

P^: ‘*’;

…..

end;

Использование идентификатора указателя означает обращение к адресу ячейки памяти, на которую он указывает.

Чтобы обратиться к содержимому ячейки на которую указывает указатель требуется поставить символ ^.

Это операция называется – разыменованием.

Указательная переменная P может быть:

  1. Содержать адрес какой – либо переменной 2n память под которую уже выделена

Адрес

P P^

2)Содержать специальный пустой адрес nil

Nil

P

3)Находится в неопределенном состоянии

?

P

?

4)Различие между состоянием nil и неопределенным состоянием

P1 P2 P1 P2

Nil

Nil

Nil

Nil

? ?

P1=P2 P1 < > P2

В таблице 6 представлены Простейшие действия с указателями

Процедурой New(A) выделяется область памяти соответственно тому типу,который описан для указателя А и записывает адрес выделенной памяти в указатель.

Процедура Dispose(A) освобождает область памяти, на которую указывает указатель А, после чего эта область памяти становится доступной для распределения под другие динамические переменные.

таблица 6 Простейшие действия с указателями

Задача 1.

Запишите программу которая выводит Сегмент (часть адреса) – начало её кода, начало данных и кучи, конца кучи, Сегмент границы свободной части кучи и стека.

Запустить программу из TP и выйдя из него

Program Ku;

Uses crt;

BEGIN clrscr;

writeln('Функция CSeg и DSeg возвращает значение из регистра CS и DS...',CSeg,' ',DSeg);

writeln('Начало кучи ',Seg(HeapOrg^),' Конец кучи ',Seg(HeapEnd^));

writeln(Seg(HeapPtr),' Края занятые частью кучи');

writeln('Адрес границы свободной части сегмента стека ',SSeg);

readln;

END.

4.2.2 Использование абсолютных адресов.

Функция Seg (<Имя>) (Сегмент)

Ofs(<Имя>) (Сегмент)

Речь идёт о первом байте значения или коде.

Можно узнать где транслятор разместил блоки и переменные программы. Можно разместить переменную <Имя > по выбранному <Адресу> указав константные выражения путём обьявления

Var <Имя>:<Тип>Absolute<адрес>

Var Z:Byte Absolute $F0000:$FFFF;

В место адреса после Absolute можно записать имя другой переменой и произойдет совмешение.

Var A,B,C:Real;

D:Array[1..5] jf Real Absolute A; D[1]- A, D[2] – B, D[3] – C; тронслятор выделяет для А,В,С, соединение 4- байтные участки памяти.

Имеются три глобальных перекрывающих массива Mem,MemW,MemL.

Элементы массива покрывают всю память имея тип Byte (массив MemL),

Для Mem и для элементов массивов MemW MemL в качестве индексов используются адреса (неномера).

Состовляющие Адреса – это выражения типа Word.

Задача 2. Запишите программу с определением константы:

ConstX:Longint=2147483000;

и выводом соответствующих ей элементов массивов MemL запишите константы и MemW: Program Ku2;

Uses crt;

Const X: Longint=2147483000;

Bedin clrscr;

Writeln(MemL[seg(x):ofs(x)],’ ’,MemW[seg(x):ofs(x)]);

End.

4.2.3 Описание типизированных и нетипизированных указателей.

  1. Описание нетипизированных указателей

Var <Список имен указателей> :Pointer

В первом случае не важен характер объекта ссылки указатель обозначает пункт памяти.

Описание типизированных указателей

2) Var <Список имен указателей >: ^ <имя базового типа>

Во втором случае

2.1) Type Vec=Array [1..5] of Real; Pnt=^Word;

Var S:^Byte; SS:^Vec; SSS:Pnt ( 3 переменных ссылочного типа )

В описании SS используется ранее описанный ссылочный тип Pnt.

2.2) Var T:^Array[1..5] of Real; TT:^Array[1..5] of Real тождественность типов T и TT транслятор не установит и Т:TT или TT:T невозможна Ситуация изменяется если определить для T и TT единый ссылочный тип

Type Pt=^Array[1..5] of Real

Var T,TT:Pt.

Вывод:

  1. Указатели можно сравнивать на равенство (неравенство).

  2. Указатели занимают 4 байта памяти (сегмент + смещение).

  3. Имя сегмент Sg и Смещение Sm, значение указателя получают с помощью функции Ptr(Sg,Sm)

  4. Операция @<Имя блока или переменная> возвращает указатель начала блока или поля переменной тот же результат функции Addr.

4.2.4 Стандартные процедуры NEW и DISPOSE

Для порождения динамических обьектов предназначена стандартная процедура NEW(A) где A – параметр ссылочного типа.

После работы процедуры NEW(A) переменная A получает значение ссылки на порожденный обьект.

Чтобы получить доступ к динамически выделенной памяти, необходимо написать “A^,

что означает “ идти по адресу хранящемуся в A^

Например

Progaram E2;

Var x,y:^Integer;

…..

NEW(x); x^:=13; y:=x; Writeln(y^); Writeln(x^+20); Dispose (y); Dispose (x);

…………

Сначала динамически порождается объект целого типа. Это означает, что выделяется место в памяти, но ничего туда не заносится.

Х - переменная ссылочного типа, ей присваивается адрес динамического объекта

X^:=13 заносит число 13 в динамическую переменную

Y- cссылочная переменная может содержать адрес с любого объекта целого типа поэтому Y:=X

Он заносится в переменную Y значение того же адреса, что храниться в переменных, первый оператор выводит 13, второй 33

1

Адрес

13

Адрес

2

Но при этом целочисленное значение хранящиеся в динамической переменой не изменяются .

Для уничтожения динамических обьектов служит стандартная процедура DISPOSE(x)

где – x переменная ссылочного типа.

После окончания работы процедуры ссылочной переменной X присваивается значение Nil, а память занимаемая динамической переменной освобождается.

Списки. стр.98 Абрамов Нач.прог. Пример:

Program KU;

Uses crt;

Type pint:^integer; Var i:Integer; p,g:pint;

Begin clrscr;

New(p); New(g); i:=5;

p^:=7; g^:=p^-i; i:=i+1; p^:=g^+I; g:=p; Write(g^);

End;

1. Ситуация 2. После выполнения оператора New(p),New(g)

i:=5

i

5

i

p

p

g g^

g

- переменные i,p,g получили значения

3. Получили и некоторые 4.Теперь значения p и g

значения и переменные совпадают

p^,g^ и изменилось

значение i.

6

6

i i p^g^

P^

p p

2

g g

Ответ 8.

То есть значение обеих переменных является ссылкой на одно и то же место в памяти. Место в памяти, ссылка на которое прежде являлась значением переменой g, теперь стало недоступным.

Это место в памяти называется “Мусор”.

4.2.5 Процедуры GetMem, FreMem. Переменный размер структур.

GetMem (<Указатель>,<Размер поля в байтах>) запрашивает поле указанного размера и в случае успеха возврвщает ссылку на него –значение<Указателя>.

Он может быть типизированным и не типизированным.

FreMem (<Указатель>,<Размер в байтах>), освобождается поле, заданным <Указателем> возвращает память в кучу.

Эти процедуры дают способ построения структур с переменным числом компонентов.

Размер поля, выделенного с помощью процедуры New и GetMem, всегда доводится до кратного 8 (увеличивается, если не кратное) те начальные адреса сегментов ДРП так же кратны 8.

Пример: Создадим динамический массив из чисел типа Word, для проверки созданной структуры заполним массив числами 1,2 .. n , а затем выведем первый и последний элемент. Начальная строка программы позволяет легко перейти к элементам иного типа

Например, если они имеют тип Real, запишем в 1-й строке Type Tip = Real; .

………………..

Type Tip = Word;

Const m = 65520 div SizeOf(Tip); {Предельное число элементов)

Type Vec = Array[1..m] of Tip;

Pt = ^ Vec; {Ссылочный тип с именем Pt}

Var j,n: Word; Ss: Pt; {Ss ~ типированный указатель}

BEGIN {Ниже записан ввод числа элементов динамического массива} Repeat Writeln('Вводите n'); Readln(n) Until (n>0) And (n<=m); GetMem ( Ss, n*SizeOf(Tip)); {Выделяется поле размером 2п}

For j:=1 to n do Ss^ [0] := j; {Занесение значений 1, 2, ... , n}

Writeln (Ss^[1],' ', Ss^[n]); {Выводим 1-й и последний элементы}

FreeMem (Ss, n*SizeOf(Tip));

Readln;

END.

Взамен контроля значения n в цикле Repeat можно было бы дать переменной n тип 1..т; и перед вводом n включить дирек­тивой {$R+} контроль выхода из диапазона.

Сделав описание типа с именем Vec, мы не потратив ни одно­го лишнего байта, получаем широкий диапазон значений n, но фактически сводим на нет автоконтроль выхода индекса из диа­пазона 1..n, В самом деле, обращение к элементу SS^[j], когда n < j < 65520 div SizeOf(Tip), не будет воспринято как "крими­нал" даже в случае действия директивы ,{$R+h} хотя элемента этого нет и вместо него нечаянно-негаданно обрабатывается или иная переменная, или вообще "пустота". Последствия непред­сказуемы.

При использовании структур с переменным размером следует вводить в программе собственный контроль выхода из диапазона.

4.2.7 Вопросы для самопроверки

  1. Что означает A:=NIL.

  2. Для чего предназначена процедура NEW(A).

  3. Что такое память.

  4. Что такое параграф.

  5. Что такое адрес и из чего он состоит.

  6. Что такое «Куча».

  7. Что дает директива $M.

  8. Что такое указатель.

  9. Особенности объявления данных динамической структуры.

  10. Процедуры GetMem, FreeMem.