Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
классы_лекция.doc
Скачиваний:
1
Добавлен:
01.05.2025
Размер:
111.62 Кб
Скачать

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

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

Tуре

ТМуСIаss = сlass

...

aIntField: Integer;

aStrField: String;

aObjField: TObject;

...

end;

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

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

type

TMyClass = class

FlntField: Integer;

FStrField: String;

...

end;

var

aObject: TMyClass;

begin

...

aObject.FIntField := 0;

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

...

end;

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

Методы

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

type

TMyClass = class

Function MyFunc(aPar: Integer): Integer;

Procedure MyProc;

end;

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

var

aObject: TMyClass;

begin

...

aObject.MyProc;

...

end;

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

type

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 с логическим параметром:

type

TVisualObject = class(TWinControl)

Procedure Hide;

Procedure Show;

Procedure Draw(IsShow: Boolean); virtual;

end;

TVisualChildObject = class(TVisualObject)

Procedure Draw(IsShow: Boolean); override;

end;

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

Procedure TVisualObject.Show;

begin

Draw(True);

end;

Procedure TVisualObject.Hide;

begin

Draw(False);

end;

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

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

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

type

TVisualObject = class(TWinControl)

...

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

end;

TVisualChildObject = class(TWinControl)

...

Procedure Draw(IsShow: Boolean); override;

end;

var

aVisualObject: TVisualObject;

aVisualChild: TVisualChildObject;

begin

...

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

методу}

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

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

...

end;

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

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

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

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

type

TMyClass = class

IntField: Integer;

Constructor Create(Value: Integer);

Destructor Destroy;

end;

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

var

MyObject: TMyClass;

begin

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);

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

begin

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

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

end;

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

type

TMyClass = class(TObject)

class Function GetClassName: String;

end;

var

S: String;

begin

S := TMyClass.GetClassName;

...

end;

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