Архитектура и строительство

Одноименные методы

Begin

Var

Type

Begin

Begin

Var

Type

Begin

Var

Type

Begin

Begin

Type

Type

Begin

Var

Type

Методы

Begin

Var

Type

Tуре

Поля

Полиморфизм

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

Поведенческие свойства класса (его функциональность) определяются набором входящих в него методов. Изменяя алгоритм того или иного метода в потомках класса, можно придавать этим потомкам отсутствующие у родителя специфические свойства.

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

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

Составляющие класса

Полями называются инкапсулированные в классе данные. Поля могут быть любого типа, в том числе — классами, например:

ТМуСIаss = сlass

aIntField: Integer;

aStrField: String;

aObjField: TObject;

end;

Каждый объект получает уникальный набор полей, но общий для всех объек­тов данного класса набор методов и свойств.

Фундаментальный принцип инкап­суляции требует обращаться к полям только с помощью методов и свойств класса. Однако в Delphi разрешается обращаться к полям и напрямую:

TMyClass = class

FlntField: Integer;

FStrField: String;

end;

aObject: TMyClass;

aObject.FIntField := 0;

aObject.FStrField := ‘Строка символов’;

end;

Класс-потомок получает все поля всех своих предков и может дополнять их своими полями, но он не может переопределять их или удалять.

Инкапсулированные в классе процедуры и функции называются методами. Они объявляются так же, как и обычные подпрограммы:

TMyClass = class

FunctionMyFunc(aPar: Integer): Integer;

ProcedureMyProc;

end;

Доступ к методам класса, как и к его полям, возможен с помощью составных имен:

aObject: TMyClass;

aObject.MyProc;

end;

Методы класса могут перекрываться в потомках. Напри­мер:

TParentClass = class

Procedure DoWork;

end;

TChildClass = class(TParentClass)

Procedure DoWork;

end;

Потомки обоих классов смогут выполнять сходную по названию процедуру DoWork, но, будут это делать по-разному. Такое перекрытие мето­дов реализуется компилятором и называется статическим.

В Delphi чаще используется динамическое перекрытие методов на эта­пе прогона программы.

Для этого метод, перекрываемый в родитель­ском классе, должен объявляться как динамический (с директивой dynamic) или виртуальный (virtual). Встретив такое объявление, компилятор создает две таб­лицы — DМТ (Dynamic Method Таblе) и VМТ (Virtual Method Таblе) и помещает в них адреса точек входа соответственно динамических и виртуальных методов. При каждом обращении к перекрываемому методу компилятор вставляет код, по­зволяющий извлечь из той или иной таблицы адрес точки входа в подпрограмму.

В классе-потомке перекрывающий метод объявляется с директивой override (перекрыть). Получив это указание, компилятор создает код, который на этапе прогона программы помещает в родительскую таблицу точку входа метода класса-­потомка, что позволят родителю выполнить нужное действие с помощью нового метода.

//

Например, пусть родительский класс с помощью методов Show и Hide соот­ветственно показывает что-то на экране или прячет изображение. Для создания изображения он использует метод Draw с логическим параметром:

TVisualObject = class(TWinControl)

Procedure Hide;

Procedure Show;

ProcedureDraw(IsShow: Boolean); virtual;

end;

TVisualChildObject = class(TVisualObject)

Procedure Draw(IsShow: Boolean); override;

end;

Реализация методов Show и Hide:

Procedure TVisualObject.Show;

Draw(True);

end;

Procedure TVisualObject.Hide;

Draw(False);

end;

Методы Draw у родителя и потомка имеют разную реализацию и создают разные изображения. В результате родительские методы Show и Hide будут прятать или показывать те или иные изображения в зависимости от конкретной реализации метода Draw у любого из своих потомков. Динамическое связывание в полной мере реализует полиморфизм классов.

загрузка…

Разница между динамическими и виртуальными методами заключается в том, что таблица динамических методов (DМТ) содержит адреса только тех методов, которые объявлены с директивой dynamic в данном классе, в то время как таблица VМТ содержит адреса виртуальных методов не только данного класса, но и его родителей. Значительно большая по размеру таблица VМТ обеспечивает быстрый поиск, в то время как при обращении к динамическому методу программа сначала просматривает таблицу DМТ у объекта, затем — у его родительского класса и так далее, пока не будет найдена нужная точка входа.

Динамически перекрываемые методы часто могут вообще ничего не делать. Такие методы называются абстрактными, они обязаны перекрываться в потомках. Можно запретить вызов абстрактного метода, объявив его с директивой abstract. Например:

TVisualObject = class(TWinControl)

Procedure Draw(IsShow: Boolean); virtual; abstract;

end;

TVisualChildObject = class(TWinControl)

Procedure Draw(IsShow: Boolean); override;

end;

aVisualObject: TVisualObject;

aVisualChild: TVisualChildObject;

aVisualObject.Show; {Ошибочное обращение к абстрактному

методу}

aVisualChild.Show; {Верное обращение. Метод Draw

у класса TVisualChildObject перекрыт.}

end;

Обращение к неперекрытому абстрактному методу вызывает ошибку периода исполнения. Классы, содержащие абстрактные методы, называются абстрактными. Такие классы инкапсулируют общие свойства своих неабстракт­ных потомков, но объекты абстрактных классов никогда не создаются и не использу­ются. Для эксплуатации абстрактных классов в библиотеку классов Delphi вклю­чаются классы-потомки, в которых перекрываются абстрактные методы родителя.//

В состав любого класса входят два специальных метода — конструктор и де­структор. У класса TObject эти методы называются Create и Destroy, также они называются в подавляющем большинстве его потомков.

Конструктор распре­деляет объект в динамической памяти (размещаются лишь поля объекта, его мето­ды являются общими для всех объектов данного класса и в кучу не переносятся) и помещает адрес этой памяти в переменную Sеlf, которая автоматически объяв­ляется в классе. Обращение к конструктору должно предварять любое обращение к полям и некоторым методам объекта.

Деструктор удаляет объект из кучи. Конструкторы и деструкторы являются процедурами, но объявляют­ся с помощью зарезервированных слов Constructor и Destructor:

TMyClass = class

IntField: Integer;

Constructor Create(Value: Integer);

Destructor Destroy;

end;

Любые поля объекта, а также методы класса, оперирующие с его полями, могут вызываться только после создания объекта путем вызова конструктора, так как конструкторы распределяют объект в динамической памяти и делают действитель­ным содержащийся в объекте указатель и автоматически объявляемую перемен­ную Self.

MyObject: TMyClass;

MyObject.IntField := 0; {Ошибка! Объект не создан

конструктором! }

MyObject := TMyClass.Create; { Правильное обращение:

создается объект}

MyObject.IntField := 0; {Затем можно обращаемся к его

полю}

MyObect.Free; // Уничтожаем ненужный объект

end;

Обращение к деструктору объекта будет ошибочным, если объект не создан конст­руктором, поэтому для уничтожения объекта следует вызывать метод Free, который определен в базовом классе TObject.Он сначала проверяет действительность адреса объекта и лишь, затем вызывает деструктор Destroy.

Специально для уничтожения объектов в модуле System определена процеду­ра FreeAndNil, которая не только уничтожает объект, но и помещает в его указа­тель (им является идентификатор объекта) значение NIL:

FreeAndNil(MyObject)

Большинство конструкторов классов реализуют некоторые действия, необходимые для правильной работы его объекта. Поэтому в конструкторе класса-потомка сна­чала вызывается конструктор своего родителя, а уже затем осуществляются дополни­тельные действия.

Вызов любого метода родительского класса достигается с по­мощью зарезервированного слова — Inherited (унаследованный):

Constructor TMyClass.Create(Value: Integer);

// Возможная реализация конструктора

Inherited Create; // Вызываем унаследованный конструктор

IntField :=Value; // Реализуем дополнительные действия

end;

Некоторые методы могут вызываться без создания и инициализации объекта. Такие методы называются методами класса, они объявляются с помощью зарезер­вированного слова class:

TMyClass = class(TObject)

class Function GetClassName: String;

end;

S: String;

S := TMyClass.GetClassName;

end;

Методы класса не должны обращаться к полям, так как в общем случае вызы­ваются без создания объекта, а, следовательно, в момент вызова полей просто не существуют. Обычно они возвращают служебную информацию о классе – имя класса, имя его родительского класса, адрес метода и т. п.

В Delphi можно в рамках одного класса объявлять несколько одно­именных методов. Механизм перекрытия родительского метода одноименным методом потомка приводит к тому, что потомок «не видит» пере­крытый родительский метод и может обращаться к нему лишь с помощью зарезер­вированного слова Inherited.

В Delphi введено зарезервированное слово over­load (перезагрузить), с помощью которого становятся видны одноименные методы, как родителя, так и потомка. Чтобы одноименные методы можно было отличить друг от друга, каждый из них должен иметь уникальный набор параметров.

В ходе выполнения программы при обращении к одному из одноименных методов программа проверяет тип и количество фактических параметров обращения и выбирает нужный метод.

При обнаружении одноименного метода компилятор Delphi предупреждает о том, что у класса уже есть аналогичный метод с другими параметрами. Для подавления сообщений объявление одноименного метода можно сопровождать зарезервированным словом reintroduce(вновь ввести).

Например, поместим на пустую форму четыре кнопки TButton и напишем для них такие обработчики их событий OnClick:

procedureTForml.Button1Click(Sender: TObject);


Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *