- •Предисловие
- •Основы программирования
- •Понятие алгоритма.
- •Алгоритм Евклида.
- •Задача о поездах и мухе
- •Вместо лирического отступления
- •Этапы подготовки задачи для решения на компьютере
- •Примеры разработки алгоритмов
- •Решение квадратного уравнения.
- •Вычисление интегралов
- •Обработка результатов эксперимента
- •Решение системы линейных алгебраических уравнений
- •Введение в язык программирования Pascal
- •Основные элементы языка
- •Переменные. Стандартные типы.
- •Операции отношения
- •Раздел описаний переменных
- •Выражения. Порядок выполнения операций.
- •Константы
- •Комментарии в программе
- •Операторы
- •2.1.7.1. Оператор присваивания
- •2.1.7.2. Операторы ввода/вывода
- •2.1.7.3. Операторы инкремента и декремента
- •Среда разработки Lazarus
- •Русский язык в консольных приложениях
- •Первая программа
- •Открытие существующего проекта
- •Другие способы создания консольных приложений
- •Типовой пустой проект
- •Операции с целыми числами
- •Вместо лирического отступления 2
- •Стандартные функции с целыми аргументами
- •Операции с вещественными числами (тип real).
- •Форматирование вывода
- •Одновременное использование вещественных и целых чисел.
- •Другие стандартные функции с вещественными аргументами
- •Булевы переменные
- •Условные операторы.
- •2.1.22.1 Оператор if …. then
- •2.1.22.2. Оператор if …then ... else
- •Операторы цикла
- •2.1.23.1. Оператор цикла с предусловием
- •2.1.23.2. Оператор цикла с постусловием
- •2.1.23.3. Оператор цикла с параметром.
- •2.1.23.4. Второй вариант оператора цикла с параметром
- •Оператор выбора case
- •Организация простейшего контроля ввода данных.
- •Вычисление сумм сходящихся рядов
- •Реализация некоторых алгоритмов главы 1.
- •Программа решения задачи о поездах и мухе
- •Программа вычисления определенного интеграла
- •Более сложные элементы языка
- •Общая структура Паскаль – программы
- •Процедуры и функции
- •3.1.1.1 Структура процедуры
- •3.1.1.2. Структура функции
- •3.1.1.3 Глобальные и локальные переменные
- •3.1.1.4 Способы передачи параметров
- •3.1.1.5 Процедуры завершения
- •Еще раз о типах данных
- •Классификация типов данных
- •3.2.1.1 Целый тип
- •3.2.1.2. Интервальный тип
- •3.2.1.3. Перечислимый тип
- •3.2.1.4. Множества
- •3.2.1.5. Логический тип
- •3.2.1.6. Вещественный тип
- •3.2.1.7. Указатели
- •Обработка символьной информации в Паскале
- •Символьные и строковые типы данных.
- •3.3.1.1. Тип Char
- •3.3.1.2. Функции для работы с символами
- •3.3.1.3. Тип String
- •3.3.1.4. Строковые процедуры и функции
- •Массивы
- •Динамические массивы
- •Программа решения системы линейных алгебраических уравнений методом Гаусса
- •3.4.1.1. Вариант 1 – с goto
- •3.4.1.2. Вариант 2 – без goto
- •3.4.1.3. Вариант 3 – наилучшая реализация
- •Модули в Паскале
- •Структура модуля
- •Системные модули
- •3.5.2.1. Модуль CRT
- •Файлы
- •Тип данных – запись
- •Файловые типы
- •Процедуры для работы с файлами
- •3.6.3.1. Общие процедуры для работы с файлами всех типов
- •3.6.3.2. Процедуры для работы с текстовыми файлами
- •3.6.3.3. Процедуры для работы с типизированными файлами
- •3.6.3.4. Процедуры для работы с нетипизированными файлами
- •3.6.3.5. Организация контроля ввода/вывода при работе файлами
- •3.6.3.6. Создание простой базы данных с типизированными файлами.
- •Алгоритмы сортировки
- •Обменная сортировка (метод "пузырька")
- •Сортировка выбором
- •Сортировка вставками
- •Метод быстрой сортировки
- •Алгоритмы поиска
- •Поиск в массивах
- •Вставка и удаление элементов в упорядоченном массиве
- •Динамические структуры данных
- •Представление в памяти компьютера динамических структур.
- •Реализация стека с помощью массивов
- •Указатели
- •Стандартные операции с линейными списками
- •Реализация динамических структур линейными списками
- •4.3.6.1. Реализация стека
- •4.3.6.2. Реализация очереди с помощью линейного списка
- •4.3.6.3. Реализация двоичного дерева с помощью линейного списка
- •Сортировка и поиск с помощью двоичного дерева
- •Три источника и три составные части ООП.
- •Классы и объекты.
- •Обращение к членам класса.
- •Инкапсуляция
- •Спецификаторы доступа.
- •Свойства.
- •Наследование
- •Полиморфизм
- •Раннее связывание.
- •Позднее связывание.
- •Конструкторы и деструкторы.
- •Элементы графического интерфейса
- •Различия между консольными и графическими приложениями
- •Визуальное программирование в среде Lazarus
- •Создание графического приложения
- •Форма и ее основные свойства
- •Компоненты
- •Обработчики событий
- •Простейшие компоненты
- •6.3.5.1. Компонент TLabel
- •6.3.5.2. Кнопки TButton, TBitBtn и TSpeedButton
- •6.3.6.1. Компонент TEdit
- •6.3.6.2. Компонент TLabeledEdit
- •6.3.7.1. Компонент TMaskEdit
- •Специальные компоненты для ввода чисел
- •Тестирование и отладка программы
- •Компоненты отображения и выбора данных
- •6.3.10.1. Компонент TMemo
- •6.3.10.2. Компонент TStringGrid
- •6.3.10.3. Компоненты выбора
- •Компонент TListBox
- •Компонент TComboBox
- •Компоненты выбора – переключатели
- •6.3.10.4. Компоненты отображения структурированных данных
- •Компонент TTreeView
- •Компонент TListView
- •Организация меню. Механизм действий - Actions
- •6.3.11.1. Компонент TMainMenu
- •6.3.11.2. Компонент TToolBar
- •6.3.11.3. Компонент TActionList
- •6.3.11.4. Создание приложений с изменяемыми размерами окон
- •Послесловие
- •Литература
- •Алфавитный указатель
5.5. Полиморфизм
____________________________________________________________________
кода. Что, надо копировать весь похожий код в каждый из дочерних методов?
Конечно же, нет! Оказывается можно вызывать метод родительского класса с помощью ключевого слова inherited. Например:
function TStudent.GetData: string; begin
Result:= inherited GetData;
Result:= Result + ', группа ' + group;
end;
Здесь сначала вызывается метод родительского класса, а затем добавляется новый код.
5.5.2 Позднее связывание.
Выше мы видели, что для статических методов разрешение связей, т.е. оп-
ределение того, какой именно метод следует вызывать, происходит на этапе компиляции. Но бывают ситуации, когда компилятор не может определить, к
какому объекту (экземпляру класса) относится метод. Рассмотрим пример.
Пусть нам необходимо, чтобы на экран выводилась кроме прежней информа-
ции еще и статус человека, т.е. кто он – студент или преподаватель. Для этого введем новый метод Status. Этот метод будет вызываться из метода
GetData. Во время компиляции неизвестно, объект какого класса вызывает метод Status. Это определяется на этапе выполнения, когда точно известен активный в данный момент объект. Разрешение связей на этапе выполнения на-
зывается поздним связыванием. Для реализации механизма позднего связыва-
ния применяются ключевые слова virtual и override. Метод родительско-
го класса помечается ключевым словом virtual. Методы дочерних классов,
перекрывающих метод родительского класса помечаются ключевым словом
442
Глава 5 Основы объектно-ориентированного программирования
____________________________________________________________________
override, а все эти методы, включая и родительский, называются виртуаль-
ными методами. Причем в отличие от статических методов, количество, тип и порядок следования параметров в виртуальных методах должны совпадать.
Рассмотрим пример.
unit Unit1;
{$mode objfpc}{$H+} interface
uses
Classes, SysUtils; type
{ THuman } THuman = class
private
Fname: string;
Ffam: string;
procedure Setname(const AValue: string); procedure Setfam(const AValue: string);
public
property name: string read Fname write Setname; property fam: string read Ffam write Setfam; function GetData: string;
function Status: string; virtual; end;
implementation { THuman }
procedure THuman.Setname(const AValue: string); begin
if Fname=AValue then exit;
443
5.5. Полиморфизм
____________________________________________________________________
Fname:=AValue;
end;
procedure THuman.Setfam(const AValue: string); begin
if Ffam=AValue then exit; Ffam:=AValue;
end;
function THuman.Status: string; begin
end;
function THuman.GetData: string; begin
Result:= name + ' ' + fam + Status; end;
end.
program project1; {$mode objfpc}{$H+} uses
CRT, FileUtil, Unit1; type
TStudent = class(THuman) private
group: string;
444
Глава 5 Основы объектно-ориентированного программирования
____________________________________________________________________
public
function GetData: string; function Status: string; override;
end;
function TStudent.Status: string; begin
Result:= ' - студент'; end;
function TStudent.GetData: string; begin
Result:= inherited GetData + ', группа ' + group; end;
type
TProfessor = class(THuman) private
kafedra: string; public
function GetData: string; function Status: string; override;
end;
function TProfessor.Status: string; begin
Result:= ' - преподаватель'; end;
function TProfessor.GetData: string; begin
445
5.5. Полиморфизм
____________________________________________________________________
Result:= inherited GetData + ', кафедра ' + kafedra; end;
var
Student: TStudent; Professor: TProfessor; fname: string;
begin
Student:= TStudent.Create; Professor:= TProfessor.Create; Student.name:= 'Виталий'; Student.fam:= 'Петров'; Student.group:= '"ПОВТАС-1/09"'; fname:= Student.GetData;
writeln(UTF8ToConsole('Это: ' + fname));
Professor.name:= 'Иван';
Professor.fam:= 'Иванов';
Professor.kafedra:= '"Программирование"';
fname:= Professor.GetData;
writeln(UTF8ToConsole('Это: ' + fname)); writeln(UTF8ToConsole('Нажмите любую клавишу'));
readkey;
Student.Free;
Professor.Free;
end.
Кроме виртуальных методов могут быть объявлены и так называемые ди-
намические методы. Они объявляются с помощью ключевого слова dynamic.
446
Глава 5 Основы объектно-ориентированного программирования
____________________________________________________________________
А в классах наследниках все того же ключевого слова override. С точки зре-
ния наследования, методы этих двух видов одинаковы: они могут быть пере-
крыты в дочернем классе только одноименными методами, имеющими тот же тип. Разница заключается в реализации механизма позднего связывания. Для виртуальных методов используется специальная таблица виртуальных методов
(ТВМ), а для динамических методов таблица динамических методов (ТДМ).
В ТВМ хранятся адреса всех виртуальных методов класса, включая и ро-
дительских классов, даже если они не перекрыты в данном классе. Поэтому вы-
зовы виртуальных методов происходят достаточно быстро, однако ТВМ требу-
ет больше памяти.
С другой стороны, каждому динамическому методу присваивается уни-
кальный индекс. В ТДМ хранятся индексы и адреса только тех динамических методов, которые описаны в данном классе, поэтому ТДМ занимает меньше памяти. При вызове динамического метода, происходит поиск в ТДМ данного класса. Если поиск не дал результатов, поиск продолжается в ТДМ всех клас-
сов-родителей в порядке иерархии. Чем больше глубина иерархии классов, тем медленнее будут работать вызовы динамических методов.
Чтобы реализовать динамические методы для нашего примера, достаточно заменить слово virtual, на dynamic.
Посмотрим теперь на код метода Status в родительском классе. Факти-
чески в этом методе ничего не делается, функция возвращает пустую строку. В
таких случаях удобнее объявить метод как абстрактный с помощью ключевого слова abstract. Реализация метода, который объявлен как абстрактный, в
данном классе не производится, но обязательно должен быть переопределен в дочерних классах. Обратите внимание, только виртуальные методы могут быть объявлены абстрактными. Для нашего примера чтобы объявить абстрактный метод добавьте в классе THuman в объявление метода Status после слова virtual через точку с запятой ключевое слово abstract и удалите код реа-
лизации метода.
447