
- •Любой алгоритм, какой бы он сложный не был, содержит лишь три основных, или, иначе говоря, базовых структуры. Этими базовыми структурами являютия:
- •Любой алгоритм, какой бы он сложный не был, содержит лишь три основных, или, иначе говоря, базовых структуры. Этими базовыми структурами являютия:
- •Клавиши перемещения курсора:
- •Работа с блоками текста
- •Основные клавиши работы с Турбо-Паскалем:
- •Клавиши перемещения курсора:
- •Работа с блоками текста
- •Основные клавиши работы с Турбо-Паскалем:
- •Клавиши перемещения курсора:
- •Работа с блоками текста
- •Основные клавиши работы с Турбо-Паскалем:
- •Строковый тип данных
- •Описание констант
- •Операция присваивания
- •Приоритет операций
- •Оператор присваивания
- •Совместимость и преобразование типов
- •Элементарный ввод-вывод
- •Лекция 5 Выражения
- •Математические операции
- •Логические операции
- •Операции отношения
- •Приоритет операций
- •Основные математические функции
- •Уменьшает значение числа X на y. Если число y не указано, то уменьшение происходит на 1. Циклы
- •Арифметические циклы
- •Итерационные циклы с предусловием
- •Итерационные циклы с постусловием
- •Операторы завершения цикла
- •Операторы ветвления и цикла Условный оператор if
- •Оператор множественного выбора Case
- •Операторы цикла
- •Операторы ветвления и цикла
- •Условный оператор if
- •Оператор множественного выбора Case
- •Операторы цикла
- •Лекция 7. Подпрограммы: процедуры и функции
- •Пример 27
- •Обмен данными
- •4.2. Множества
- •4.2.1. Объявление типа множества
- •4.2.2. Операции над множествами
- •4.2.3. Пример использования множества
- •Пример 33
- •4.3. Строки
- •4.3.1. Объявление типа String
- •4.3.2. Операции над строковыми переменными
- •4.3.3. Встроенные процедуры и функции обработки строк
- •Пример 34
- •4.1. Матрица
- •4.1.1. Ввод-вывод элементов матрицы
- •4.1.2. Определение индексов элементов матрицы
- •Записи Объявление типа записи
- •Вложенные записи
- •Массивы записей
- •Пример обработки массива записей
- •Определение и особенности файлов
- •Доступ к компонентам файла
- •Текстовый файл
- •Чтение из текстового файла
- •Запись в текстовый файл
- •Файлы в Тубо Паскаль
- •Файловые процедуры и функции
- •Текстовые файлы
- •Типизированные файлы
- •Нетипизированные файлы
- •Графика Турбо Паскаля
- •Записи Объявление типа записи
- •Вложенные записи
- •Массивы записей
- •Пример обработки массива записей
- •Статические и динамические памяти переменные
- •Объявление указателей
- •Выделение и освобождение динамической памяти
- •Операции с указателем
- •Пример 41
- •Связанные списки
- •Понятие бинарные деревья. Операции над бинарными деревьями
- •Применение бинарных деревьев
- •Сравнение рекурсии и итерации
- •Пример 51
- •Реализация объектно-ориентированного подхода в турбо-паскале
- •Алгоритмическая и объектная декомпозиция
- •Объектный тип в Турбо-Паскале
- •Способы наследования и переопределения
- •Виртуальные методы
- •Модуль c r t
- •Модуль g r a p h
Виртуальные методы
Рассмотрим иерархическую структуру объектов задачи передвижения изображений, представленную на Рис.12.
Рис.12. Структура объектов
Для этой структуры был описан родительский объект TImage, задающий абстрактный элемент, и объект-потомок TSymbol, соответствующий символу. Напомним, что в объектном типе TImage определены координаты левого верхнего угла изображения (X,Y), размеры изображения (RX,RY), признак видимости изображения (V), а также следующие методы:
- Init - инициализирует координаты X,Y; - On, Off - включает/выключает изображение; - Move - передвигает изображение на экране; - GetX, GetY - определяет координаты X,Y; - PrintImage - выводит изображение на экран,
Дополним нашу задачу описанием другого изображения - строки:
type
PStroka = ^TStroka;
TStroka = object(TImage)
Str: string; {обрабатываемая строка}
procedure Init(A,B: integer; St:string);
procedure On;
procedure Off;
procedure PrintImage;
end;
procedure TStroka.Init(A,B:integer; St:string);
begin
TImage.Init(A,B);
Str := St;
end;
procedure TStroka.On;
begin
V := TRUE;
GotoXY(X,Y);
write(Str);
end;
procedure TStroka.Off;
var
Str1:string;
begin
V := FALSE;
GotoXY(X,Y);
FillChar(Str1,Length(Str)+1,' ');
{процедура заполняет побайтно переменную символом пробела, начиная с нулевого байта}
Str1[0]:=Chr(Length(Str)); {занесение длины строки в нулевой байт}
write(Str1);
end;
procedure TStroka.PrintImage;
begin
writeln('Строка: ',Str);
end;
Методы GetX, GetY, Move являются общими для обоих потомков (символа и строки) и не требуют переопределения. Методы Init, On, Off, PrintImage для каждого вида изображений являются индивидуальными и должны быть переопределены заново для каждого объекта. Поскольку объект TImage является абстракцией, тела этих методов в его определении являются пустыми. Теперь нам необходимо обеспечить настройку метода Move на тип объекта: символ или строку. Самое простое решение - традиционное: перед каждым вызовом Move производить анализ экземпляра объекта и в зависимости от результата подключать нужную процедуру On и Off. Мало того, что эта операция достаточно трудоемка, она абсолютно не соответствует концепциям ООП.
Рассмотрим более изящное решение, поддерживаемое объектно-ориентированными средствами. При обращении к методу Move экземпляр потомка вызывает унаследованный от родителя метод Move, который жестко связан с методами TImage.On, TImage.Off, т.к. они были вместе откомпилированы. Связь этих методов - статическая, поскольку она была установлена при компиляции. Чтобы разорвать статическую связь метода Move c методами TImage.On, TImage.Off, которые переопределяются для каждого объекта, объявим эти методы виртуальными. Таким образом существует возможность изменять тот или иной метод объекта в процессе выполнения программы, устанавливая так называемую динамическую связь методов. Для того, чтобы определить виртуальный метод, необходимо указать после его заголовка в родительском типе служебное слово Virtual. Переопределенные одноименные методы всех потомков должны быть также отмечены как виртуальные. Кроме того, все виртуальные методы должны иметь одинаковый набор формальных параметров. В процессе выполнения программы обеспечивается вызов виртуального метода, соответствующего вызываемому объекту, и один и тот же метод будет работать по-разному, например, метод TImage.PrintImage выводит либо символ, либо строку в зависимости от типа объекта, для которого он вызывается. В этом заключается смысл полиморфизма, когда при вызове одного и того же метода каждый объект может "заказывать" особенности реализации этого метода. Благодаря полиморфизму, удается существенно повысить эффективность разработки больших программных систем. Естественно ожидать, что выполнение программы с виртуальными методами будет происходит несколько медленнее, кроме того требуется дополнительный расход оперативной памяти.
Использование виртуальных методов накладывает дополнительное требование на объектный тип. В этом случае объект должен содержать хотя бы один метод-конструктор, определяющий процедуру создания данного объекта. Синтаксически конструктор объявляется как обычная процедура с заменой ключевого слова Procedure на зарезервированное слово Constructor. Смысл введения конструктора заключается в том, что он дополнительно формирует в экземпляре объекта необходимую информацию для последующих вызовов виртуальных методов. Конструктор должен быть применен к экземпляру объекта до первого вызова виртуального метода. Обычно в качестве конструктора выбирают метод, инициализирующий значения полей экземпляра объекта. Сам конструктор не может быть виртуальным. В нашем примере в качестве конструктора выбрана процедура Init. Теперь объектные типы должны определяться следующим образом:
type
PImage=^TImage;
TImage=object
. . .
constructor Init(A,B:integer);
procedure On; virtual;
procedure Off; virtual;
procedure Move(Dx,Dy:integer);
procedure PrintImage; virtual;
. . .
end;
PSymbol=^TSymbol;
TSymbol=object(TImage)
. . .
constructor Init(A,B:integer; C:char);
procedure On; virtual;
procedure Off; virtual;
procedure PrintImage; virtual;
end;
Мы уже отмечали, что экземпляры объектов так же, как и переменные любых других типов, можно размещать в динамической памяти с помощью процедуры New. Именно для такого размещения были определены указатели в определении объектных типов PImage, PSymbol и PStroka, например:
var
PS:PSymbol;
. . .
New(PS); {выделение памяти для экземпляра объекта}
PS^.Init(1,1,Ch);
Dispose(PS); {освобождение памяти}
В последних версиях Турбо-Паскаля допускается расширенный синтаксис процедур New и Dispose:
New(PS, Init(1,1,Ch));
Процедура New не только размещает новый экземпляр объекта в динамической памяти, но и одновременно вызывает конструктор, не используя его составного имени. В этом случае первый параметр PS однозначно определяет, из какого объектного типа берется конструктор.
В заключение следует упомянуть о методах - деструкторах, которые предназначены для выполнения завершающих действий в программе обработки объектов. Деструктор определяется как процедура, которая делает состояние объекта неопределенным и (или) ликвидирует сам объект. Например, если экземпляр объекта был динамическим, то необходимо освободить память, выделенную для его полей данных, и сделать это надо до уничтожения самого объекта. Деструкторы оформляются как обычные процедуры с заменой заголовка на ключевое слово Destructor. Деструкторы могут наследоваться. Они могут быть статическими или виртуальными, но лучше использовать виртуальные, т.к. в этом случае есть полная гарантия того, что выполнится деструктор, соответствующий данному типу объекта. Для динамических объектов всегда нужно объявлять деструктор, даже пустой:
Destructor TImage.Done;
begin end;
Теперь можно использовать расширенный вызов процедуры Dispose с деструктором:
Dispose(PS, Done);
В результате выполнятся действия, описанные в деструкторе Done (если они есть), а затем процедура Dispose освободит количество байтов памяти, соответствующее типу экземпляра объекта PS. Вызов деструктора вне процедуры Dispose естественно не приведет к освобождению памяти, занимаемой экземпляром объекта