Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
delphi / песни о паскале.pdf
Скачиваний:
63
Добавлен:
26.03.2016
Размер:
5.16 Mб
Скачать

Глава 61 «Кубики» программиста (ООП)

constructor TMilitary.Init(aBearing: integer; const aName, aFam, aRank : string);

begin

inherited Init(aBearing, aName, aFam);

{ вызов метода предка }

mRank:= aRank;

 

end;

Переходим к методу Report наследника. Здесь, вдобавок к прочим данным, надо распечатать ещё и воинское звание. Прочие данные распечатаем унаследованным методом Report, а воинское звание — дополнительным оператором печати. Вы уже догадались, что реализация метода будет такова.

procedure TMilitary.Report; begin

inherited Report;

{ вызов метода предка }

Writeln('Воинское

звание: '+mRank);

end;

Породив «военного человека», перейдём к мирному строительству: создадим объект, играющий роль гражданского служащего. Назовем его TCivil, а род его пойдет от того же предка TPerson. У гражданских своя гордость и своя служебная лестница, ступеньки которой — категории — нумеруются числами. Хранить информацию о карьерном росте будем в числовом поле, назовем его mLevel — «уровень». Так же, как и для военного, нам придется дополнить конструктор объекта и метод распечатки Report. Ход рассуждений будет прежним, а потому не буду повторять его, сделайте эту работу сами.

Сотворив наследников «человека» — объекты TMilitary и TCivil, мы почти разобрались в механизме наследования. А где же полиморфизм? В чем он проявляется? Для ответа обратимся к динамическим объектам.

Динамические объекты

Динамические переменные знакомы нам ещё с 52-й главы. Указатели на объекты ничем не отличаются от таковых для других типов данных. Например, указатель на тип TPerson объявляется так:

type PPerson = ^TPerson;

Теперь можно объявить переменную этого типа, взять для неё память в куче, а затем инициализировать поля конструктором.

512

 

 

Глава 61

 

 

 

«Кубики» программиста (ООП)

 

 

 

 

 

 

var P : PPerson;

{ указатель на объект }

 

 

begin

 

 

 

New(P);

{ выделение памяти в куче }

 

P^.Init(1985, 'Иван', 'Грозный'); { инициализация объекта }

В серьезных программах объекты обычно используют динамически, а выделение памяти и инициализацию выполняют на каждом шагу. Потому в Паскаль введена функция New, совмещающая эти действия. Функция New подобна процедуре New, но вдобавок вызывает ещё и конструктор объекта. Функция принимает два странных параметра: тип-указатель на объект и конструктор этого объекта, а возвращает указатель на созданный объект. Так, динамический объект типа TPerson может быть порожден и инициализирован одним оператором.

P:= New(PPerson, Init(1985, 'Иван', 'Грозный'));

Обратите внимание, что первый параметр функции — это тип-указатель PPerson, а не тип объекта TPerson!

Примечание. В языке Delphi и совместимом с ним режиме Free Pascal применяют иной синтаксис вызова конструктора, например:

var P : TPerson;

{ это указатель на объект! }

. . .

 

P:= TPerson.Init(1985, 'Иван', 'Грозный'); { создаём динамический объект }

Дело в том, что все объекты в Delphi — это динамические переменные, и переменная типа TPerson является указателем на объект. Для создания таких объектов применяют не функцию New, а вызов конструктора с префиксом, указывающим тип объекта.

Полиморфизм

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

var P1

: PPerson;

{ указатель на предка }

P2

: PMilitary;

{ указатель на потомка }

P3

: PCivil;

{ указатель на потомка }

 

 

 

Здесь P1 является указателем на предка, а P2 и P3 — на разных его потомков.

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

513

Глава 61 «Кубики» программиста (ООП)

P1:= P2;

P1:= P3;

Думаете, пустая формальность? Зря вы так! Воистину здесь таится глубокий смысл, поскольку через указатель на предка можно вызывать методы его потомков. Но при условии, что эти методы унаследованы от предка как виртуальные. Так, в следующем примере указателю P1 трижды присваиваются указатели на объекты разных типов: сначала на предка TPerson, а затем на двух его потомков, после чего всякий раз вызывается виртуальный метод Report. Но в реальности происходит вызов трех разных методов Report — соответственно типу объекта, на который в текущий момент ссылается указатель P1. Так срабатывает механика полиморфизма!

P1:= New(PPerson, Init(1985, 'Иван', 'Грозный'));

P1^.Report;

{ вызывается TPerson.Report }

P1:= New(PCivil, Init(1995, 'Мария', 'Рыбкина', 12));

P1^.Report;

{ вызывается TCivil.Report }

P1:= New(PMilitary, Init(1985, 'Андрей', 'Быков', 'Майор'));

P1^.Report;

{ вызывается TMilitary.Report }

Кажется, что полиморфизм одушевляет объект и делает его умнее: объект сам «понимает», как ему исполнить то, или иное желание программиста. Тот лишь вызывает нужный метод, не вникая в детали. Это похоже на управление телевизором или другим прибором. Подайте им напряжение, и все они включатся: хоть и по-разному, но каждый по-своему правильно.

Но мощная механика полиморфизма срабатывает лишь для родственных объектов, состоящих в отношении предок-потомок. Именно в таких отношениях находятся созданные нами объекты. А вот пример иного рода.

type TA = object constructor Init;

procedure Report; virtual;

end;

TB = object constructor Init;

procedure Report; virtual;

end;

Здесь объявлены два типа объектов с одноименными виртуальными методами. Но полиморфизмом тут и не пахнет, поскольку объекты не родственны меж собой!

514

Глава 61 «Кубики» программиста (ООП)

В завершение темы изучите программу P_61_3, где собрано всё, что было сказано о «человечьих» объектах.

{ P_61_3 – Демонстрация принципов наследования и полиморфизма }

uses Person;

{ Объект TPerson импортируется из модуля Person }

type PMilitary = ^TMilitary;

{ указатель на объект «ВОЕННОСЛУЖАЩИЙ» }

TMilitary = object (TPerson)

mRank

: string;

{ воинское звание }

constructor Init(aBearing: integer; const aName, aFam,

 

 

aRank : string);

procedure Report; virtual;

end;

 

 

PCivil = ^TCivil;

{ указатель на объект «ГРАЖДАНСКИЙ СЛУЖАЩИЙ» }

TCivil = object (TPerson)

mLevel

: integer;

{ должностная категория }

constructor Init(aBearing: integer; const aName, aFam : string; aLevel: integer);

procedure Report; virtual; end;

{--- Реализация объекта «ВОЕННОСЛУЖАЩИЙ» ---}

constructor TMilitary.Init(aBearing: integer; const aName, aFam, aRank : string);

begin

inherited Init(aBearing, aName, aFam); mRank:= aRank;

end;

procedure TMilitary.Report; begin

inherited Report; Writeln('Звание: '+mRank);

end;

515

Соседние файлы в папке delphi