Если предположить, что классы и объекты в ООП являются отражением понятий реальной жизни, то легко можно установить те отношения, которые могут существовать между классами. Первый тип отношений соответствует ситуации, когда один класс включает в себя объекты других классов. Такой тип отношений называется агрегированием (иначе называемым отношением has-a или отношением part-of). Данный тип отношения моделируется включением в класс полей-объектов. Например, класс для представления группы людей может иметь следующее описание:
type TCrowd = class
fPeople: array[1..100] of TPerson;
...
end;
Для классов, реализующих агрегирование, конструктор, как правило, занимается созданием объектов-полей, а деструктор уничтожает эти объекты:
constructor TCrowd.Create;
var i: Integer;
begin
for i:= 1 to 100 do
fPeople[i]:= TPerson.Create
end;
Следующий тип отношений связан с ситуацией, когда понятие, соответствующее одному классу, уточняется понятием, соответствующим другому классу. Пусть необходим класс для описания служащих – TEmployee. Можно рассуждать так: любой служащий является человеком (TPerson), но служащий – это такой человек, который получает зарплату. Отношение между классами TEmployee и TPerson называется наследованием (отношение is-a). Наследование является одним из базовых принципов ООП. Наследование предполагает создание новых классов на основе существующих. В нашем случае мы можем не писать класс TEmployee «с нуля», а воспользоваться классом TPerson как основой. При наследовании новый класс называется классом-потомком (или дочерним классом, производным классом), старый – классом-предком (или родительским классом, базовым классом). При помощи наследования можно строить так называемое дерево классов (или иерархию классов), последовательно уточняя описание класса и переходя от общих понятий к частным.
Рассмотрим синтаксис наследования классов. В Object Pascal при объявлении класса-потомка имя класса-предка указывается после ключевого слова class в круглых скобках. Описание класса-потомка включает только те элементы, которых нет в предке, так как потомок получает все элементы предка автоматически.
Описание класса TEmployee может выглядеть следующим образом:
type TEmployee = class(TPerson)
private
fSalary: double;
procedure SetSalary(Value: double);
public
property Salary: double read fSalary write SetSalary;
end;
Как наследник, TEmployee содержит все поля, методы и свойства TPerson и, кроме этого, добавляет собственное поле fSalary, метод SetSalary и свойство Salary.
Объекты классов-потомков совместимы по присваиванию с объектами классов-предков. При этом действует следующее правило: объекту родительского класса можно присвоить объект дочернего класса, но не наоборот:
var Man: TPerson; Employee: TEmployee;
Employee: TEmployee;
...
Man:= Employee; // допустимо
Employee:= Man; // ошибка компиляции
Обосновывается вышеуказанное правило следующим образом. Так как дочерний класс может добавлять к родительскому классу новые поля, то при присваивании объекту дочернего класса объекта родительского класса обращение к новым полям приведёт к выходу за границу памяти объекта родительского класса. Это является недопустимым.
Отметим следующие особенности объектной модели Object Pascal. Наследование в Object Pascal разрешено только от одного предка. Все классы в Object Pascal имеют одного общего предка. Таким предком является класс TObject. Объявления TPerson = class и TPerson = class(TObject) полностью эквивалентны. Класс TObject содержит пустой конструктор Create и пустой деструктор Destroy, которые можно применять в простых классах (этот факт использовался в некоторых примерах).
С наследованием связана директива ограничения видимости protected. Элементы класса из секции protected могут использоваться вне пределов модуля с объявлением класса, но только потомками класса.