Листинг 5.10. Использование переопределения

Unit Overriding1;

Interface

Type

TwoNums= class

public {Заголовок класса TwoNums}

a, b: Integer;{Описание двух свойств}

function GetResult: Integer; virtual;{Описание заголовка метода; после описания указано ключевое слово virtual, то есть этот метод может быть переопределен в дочернем классе}

End;

ThreeNums= class (TwoNums)

Public{Заголовок класса ThreeNums, в скобках после ключевого слова class указан класс-родитель}

с: Integer;{Описание свойства с'. Свойства а и b

наследуются от класса-родителя TwoNums}

function GetResult: Integer; override;

{Описание заголовка метода, идентичного заголовку объекта-родителя; после описания указано ключевое слово override, указывающее на переопределение функциональности родительского метода}

End;

Implementation

function TwoNums.GetResult: Integer;

{Описание метода GetResult класса TwoNums}

Begin

Result:= a + b;{Результат функции — сумма двух свойств}

end;

function ThreeNums.GetResult: Integer;{Описание переопределенного метода

GetResult класса ThreeNums}

Begin

Result := a + b + c;{Результат функции — сумма трех свойств}

end;

End.

Переопределение методов с сохранением функциональности

В приведенном примере метод GetResult класса TwoNums полностью переопределен в классе ThreeNums, то есть его изначальная функциональность полностью утеряна, но включена в функциональность замещающего метода с помощью копирования. Это практически всегда возможно, если разработчик класса имеет доступ к исходному тексту класса-родителя, но не всегда удобно, так как программный код, реализующий метод, может иметь немалые размеры.

Для сохранения функциональности переопределенного метода в Delphi имеется возможность его вызова из переопределяющего метода с помощью ключевого слова inherited,используемого следующим образом:

Inherited<Название метода>(<список параметров>);

Аналогично могут вызываться и переопределенные методы, которые являютсяфункциями:

….:= Inherited <Название метода>(<Список параметров>);

Изменим предыдущий пример таким образом, чтобы сумма свойств в методе GetResult вычислялась с использованием переопределенного вариантаэтого метода, описанного в классе TwoNums (листинг 5.11).

Листинг 5.11. Переопределение методов с сохранением функциональности

Unit0verriding2;

Interface

Type

TwoNums = class

Public

a, b: Integer;

function GetResult: Integer; virtual;

End;

ThreeNums = class(TwoNums)

Public

c: Integer;

function GetResult: Integer; override;

End;

Implementation

function TwoNums.GetResult: Integer;

result := a + b;

end;

function ThreeNums.GetResult: Integer;

Begin

Result : = Inherited GetResult + c;

{Результат функции — сумма значения, выдаваемого переопределенным методом (сумма а и Ь) , и свойства с}

end;

End.

Жизненный цикл экземпляра класса

Создание и разрушение экземпляра класса (объекта), а также работа с ним, имеет сложный внутренний механизм. Однако все действия, необходимые для поддержания жизненного цикла объекта, берет на себя компилятор. Рассмотрим последовательно стадии работы с экземпляром класса:

1. Создание экземпляра класса (объекта).

2. Использовани.

3. Разрушени.

Каждый из этапов подробно рассмотрен нами далее.

5.3.1. Создание экземпляра класса. Конструктор

Для создания экземпляра класса необходимо вызвать специализированный статический метод этого класса, называемый конструктором.Конструктор наследуется автоматически всеми классами от коренного класса TObject и выполняет две важных функции:

1. Выделяет память под структуры данных, необходимые для поддержания жизнедеятельности объекта, то есть инициализирует объект; заполняет порядковые (целочисленные, логические, перечислимые и интервальные) свойства объекта нулевыми значениями; устанавливает нулевые ссылки (значение nil)для свойств-указателей, а также устанавливает нулевые длины динамическим строкам.

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

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

Описание конструктора в классе TObject выглядит следующим образом:

ConstructorCreate;

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

Var

<Название переменной>: <Название класса>;

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

Листинг 5.12. Описание и вызов конструктора

unit Geoml;

Interface

Type

TGeomFigure= class{Заголовок класса TGeomFigure, класс автоматически является наследником класса TObject}

End;

Implementation

Procedure CreateFigure;

Var

Figure: TGeomFigure;{Описание переменной — ссылки на экземпляр класса. Несмотря на то, что не указан модификатор ^,переменная Figure является переменной-указателем}

Begin

Figure:= TGeomFigure.Create;{Вызов конструктора класса TGeomFigure, унаследованный от класса TObject, и сохранение ссылки на новый объект в переменной Figure}

end;

END.

Переопределение конструктора

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

1.Конструктор родительского класса не обязательно должен быть помечен ключевым словом virtual, для того, чтобы его переопределить. Соответственно, если конструктор родительского класса не помечен как виртуальный или динамический, не требуется указание ключевого слова override в описании дочернего класса. Виртуальные конструкторы необходимы для работы с типами данных, являющимися ссылками на классы (а не на объекты). Такая ссылка используется, например, в конструкторе класса TCollection, предназначенного в Delphi для хранения динамических списков.

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

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

Inherited;

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

Если в классе-наследнике изменен список параметров родительского конструктора (например, список расширен), конструктор родительского класса вызывается с указанием его названия и передачей ему необходимых параметров:

Inherited Create (<Список параметров>) ;

Рассмотрим пример создания объекта с переопределением конструктора (листинг 5.13).

Наши рекомендации