СОСТАВЛЯЮЩИЕ КЛАССА
9.2.1. Поля Полями называются инкапсулированные в классе данные. Поля могут быть любого типа, в том числе - классами, например: type TMyClass = class aIntField: Integer; aStrField: String; aObjField: TObject; End; Каждый объект получает уникальный набор полей, но общий для всех объектов данного класса набор методов и свойств. Фундаментальный принцип инкапсуляции требует обращаться к полям только с помощью методов и свойств класса. Однако в Object Pascal разрешается обращаться к полям и напрямую: Type TMyClass = class FIntField: Integer; FStrField: String; end; Var aObject: TMyClass; Begin aObject.FIntField:= 0; aObject.FStrField:= 'Строка символов'; End; Класс-потомок получает все поля всех своих предков и может дополнять их своими, но он не может переопределять их или удалять. Таким образом, чем ниже в дереве иерархии располагается класс, тем больше данных получают в свое распоряжение его объекты. 9.2.2. Методы Инкапсулированные в классе процедуры и функции называются методами. Они объявляются так же, как и обычные подпрограммы: 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, но, в общем случае, будут это делать по-разному. Такое замещение методов называется статическим, т. к. реализуется компилятором. В Object Pascal гораздо чаще используется динамическое замещение методов на этапе прогона программы. Для реализации этого метод, замещаемый в родительском классе, должен объявляться как динамический (с директивой dynamic) или виртуальный (virtual). Встретив такое объявление, компилятор создаст две таблицы -DMT (Dynamic Method Table) и VMT (Virtual Method Table) и поместит в них адреса точек входа соответственно динамических и виртуальных методов. При каждом обращении к замещаемому методу компилятор вставляет код, позволяющий извлечь адрес точки входа в подпрограмму из той или иной таблицы. В классе-потомке замещающий метод объявляется с директивой 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 у-любого из своих потомков. Динамическое связывание в полной мере реализует полиморфизм классов. Разница между динамическими и виртуальными методами состоит в том, что таблица динамических методов DMT содержит адреса только -тех методов, которые объявлены как dynamic в данном классе, в то время как таблица VMT содержит адреса виртуальных методов не только данного класса, но и всех его родителей. Значительно большая по размеру таблица VMT обеспечивает более быстрый поиск, в то время как при обращении к динамическому методу программа сначала просматривает таблицу DMT у объекта, затем -у его родительского класса и так далее, пока не будет найдена нужная точка входа. Динамически перекрываемые методы часто могут вообще ничего не делать. Такие методы называются абстрактными, они обязаны перекрываться в потомках. Программист может запретить вызов абстрактного метода, объявив его с директивой 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; {Ошибка/ Обращение к абстрактному методу} End; Обращение к неперекрытому абстрактному методу вызывает ошибку периода исполнения. Разумеется, в грамотно составленной программе абстрактные методы никогда не вызываются. Классы, содержащие абстрактные методы, называются абстрактными. Такие классы инкапсулируют общие свойства своих неабстрактных потомков, но объекты абстрактных классов никогда не создаются и не используются. Для эксплуатации абстрактных классов в библиотеку классов Delphi включаются классы-потомки, в которых перекрываются абстрактные методы родителя. В состав любого класса входят два специальных метода -конструктор и деструктор. У класса TObject эти методы называются create и Destroy, так же они называются в подавляющем большинстве его потомков. Конструктор распределяет объект в динамической памяти и помещает адрес этой памяти в переменную self, которая автоматически объявляется в классе. Деструктор удаляет объект из кучи. Обращение к конструктору должно предварять любое обращение к полям и некоторым методам объекта. По своей форме конструкторы и деструкторы являются процедурами, но объявляются с помощью зарезервированных слов constructor и Destructor: Type TMyClass = class IntField: Integer; Constructor Create(Value: Integer); Destructor Destroy; End; Любые поля объекта, а также методы класса, оперирующие с его полями, могут вызываться только после создания объекта с помощью вызова конструктора, т. к. конструкторы распределяют объект в динамической памяти и делают действительным содержащийся в объекте указатель. Var MyObject: TMyClass; Begin MyObject.IntField:= 0; { Ошибка! Объект не созданконструктором!} MyObject:= TMyClass.Create; // Надо так: создаем объект MyObject.IntField := 0; // и обращаемся к его полю MyObect.Free; // Уничтожаем ненужный объект End; В базовом классе TObject определен метод Free, который сначала проверяет действительность адреса объекта и лишь затем вызывает деструктор Destroy. Обращение к деструктору объекта будет ошибочным, если объект не создан конструктором, поэтому для уничтожения ненужного объекта следует вызывать метод Free, как это сделано в предыдущем примере. Большинство конструкторов реализуют некоторые действия, необходимые для правильной работы объекта. Поэтому в конструкторе класса-потомка следует сначала вызвать конструктор своего родителя, а уже затем осуществлять дополнительные действия. Вызов любого метода родительского класса достигается с помощью зарезервированного слова 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; Методы класса не должны обращаться к полям, т. к. в общем случае вызываются без создания объекта, а следовательно, в момент вызова полей просто не существует. Обычно они возвращают служебную информацию о классе - имя класса, имя его родительского класса, адрес метода и т. п. 9.2.3. Одноименные методы В отличие от остальных версий Delphi в версиях 4...6 появилась возможность в рамках одного класса иметь несколько одноименных методов. Описанный выше механизм перекрытия родительского метода одноименным методом потомка приводит к тому, что потомок “не видит” перекрытый родительский метод и может обращаться к нему лишь с помощью зарезервированного слова inherited. В Delphi 4 введено зарезервированное слово overload (перезагрузить), с помощью которого становятся видны одноименные методы как родителя, так и потомка. При обнаружении одноименного метода компилятор Delphi предупреждает о том, что у класса уже есть аналогичный метод с дру гими параметрами. Для подавления сообщений объявление одноименного метода можно сопровождать зарезервированным словом reintrpduce (вновь ввести).
методов программа проверяет; тип и количество фактических параметров обращения и выбирает нужный метод В следующем примере в классе TForm1 используются целых 4 одноименных метода close. Лишь один из них - унаследованный метод без параметра выполняет свои основные функции - закрывает окно. Три других отличаются набором параметров и выводят сообщение в заголовок окна. Поместите на пустую форму четыре кнопки TButton и напишите такие обработчики их событий OnClick: procedure TForm1.ButtonlClick(Sender: TObject); Begin Close('Строка символов') End; procedure TFormi.Button2Click(Sender: TObject); Begin Close(123) End; procedure TFormi.ButtonSClick(Sender: TObject); Begin Close (20,300); End; procedure TFormi.Button4Click(Sender: TObject); Begin Close end; Теперь в раздел private класса Tform1 вставьте три таких объявления методов close:
|