9742

Классы и объекты в Object Pascal

Реферат

Информатика, кибернетика и программирование

Классы и объекты Классами в Object Pascal называются специальные типы, которые содержат поля, методы и свойства. Как и любой другой тип, класс служит лишь образцом для создания конкретных экземпляров реализации, которые называются объектами. Сразу ж...

Русский

2013-03-15

82.5 KB

48 чел.

Классы и объекты

Классами в Object Pascal называются специальные типы, которые содержат поля, методы и свойства. Как и любой другой тип, класс служит лишь образцом для создания конкретных экземпляров реализации, которые называются объектами. Сразу же уточню, что в предшественнике Object Pascal - Turbo Pascal объектами называются типы, имеющие много общего с классами Object Pascal. Однако существенные усовершенствования, внесенные в объектную модель Object Pascal, заставили разработчиков языка ввести для обозначения объектов специальный термин - класс, заимствованный, кстати, из Си++. Для совместимости с ранее разработанными программами системы Turbo Pascal with Objects 7.0 в Object Pascal сохранен тип-объект Object, поддерживающий «старую» объектную модель. Поскольку все возможности этой модели доступны классам, мы не будем рассматривать ее, а «освободившийся» термин объект будем употреблять исключительно для обозначения конкретного экземпляра реализации класса.

Важным отличием классов от других типов является то, что объекты класса всегда распределяются в куче, поэтому объект-переменная фактически представляет собой лишь указатель на динамическую область памяти. Однако, в отличие от других указателей, при ссылке на содержимое объекта запрещается использовать символ «^» за именем объекта:

type

 TMyClass = class(TObject)

 Field: Integer;

 ...

 end;

var

 MyClass: TMyClass;

begin

 ...

 MyClass^.Field := 0;  // Ошибка!. Следует писать так:

 MyClass.Field := 0;

end;

ОСНОВНЫЕ ПОНЯТИЯ

Классы - это особое «изобретение» программистов для упрощения разработки сложных программ и улучшения их качества. В основе классов лежат три фундаментальных принципа, которые называются инкапсуляция, наследование и полиморфизм.

Инкапсуляция

Класс представляет собой единство трех сущностей - полей, методов и свойств. Объединение этих сущностей в единое целое и называется инкапсуляцией. Инкапсуляция позволяет во многом изолировать класс от остальных частей программы, сделать его «самодостаточным» для решения конкретной задачи. В результате класс всегда несет в себе некоторую функциональность. Например, класс TForm содержит (инкапсулирует в себе) все необходимое для создания Windows-окна, класс ТМеmо представляет собой полнофункциональный текстовый редактор, класс TTimer обеспечивает работу программы с таймером и т.д..

Инкапсуляция представляет собой мощное средство обмена готовыми к работе программными заготовками. Библиотека классов Delphi - это, фактически, набор «кирпичиков», созданных программистами Borland для построения ваших программ.

Наследование

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

TChildClass = class(TParentClass)

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

Все классы Object Pascal порождены от единственного родителя – класса TObject. Этот класс не имеет полей и свойств, но включает в себя методы самого общего назначения, обеспечивающие весь жизненный цикл любых объектов - от их создания до уничтожения. Программист не может создать класс, который не был бы дочерним классом TObject. Следующие два объявления идентичны:

TaClass = class(TObject)

TaClass = class

Принцип наследования приводит к созданию ветвящегося дерева классов, постепенно разрастающегося при перемещении от TObject к его потомкам. Каждый потомок дополняет возможности своего родителя новыми и передает их своим потомкам.

Для примера на рис. 1 показан небольшой фрагмент дерева классов Delphi. Класс TPersistent обогащает возможности своего родителя TObject: он «умеет» сохранять данные в файле и получать их из него, в результате это умеют делать и все его потомки. Класс TComponent, в свою очередь, умеет взаимодействовать со средой разработчика и передает это умение своим потомкам. TControl не только способен работать с файлами и средой разработчика, но он еще умеет создавать и обслуживать видимые на экране изображения, а его потомок TWinControl может создавать Windows-окна и т.д.

Рис. 1. Фрагмент дерева классов Object Pascal

Полиморфизм

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

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

СОСТАВЛЯЮЩИЕ КЛАССА

Поля

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

type

 TMyClass = class

   aIntField: Integer;

   aStrField: String;

   aObjField: TObject;

   . . .

end;

Каждый объект получает уникальный набор полей, но общий для всех объектов данного класса набор методов и свойств. Фундаментальный принцип инкапсуляции требует обращаться к полям только с помощью методов и свойств класса. Однако в Object Pascal разрешается обращаться к полям и напрямую. Для этого используются составные имена полей, содержащие имя объекта в качестве префикса:

var

 aObject: TMyClass;

begin

 aObject.aIntField := 0;

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

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, но, в общем случае, будут это делать по-разному. Такое замещение методов называется статическим, т.к. реализуется компилятором.

В 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

 aVisualObjoect: TVisualObject;

 aVisualChild: TVisualChildObject;

begin

 aVisualObject.Show;// Ошибка! Обращение к абстрактному методу

 aVisualChild.Show;// Нормальное обращение. Метод Draw у класса

                   // TVisualChildObject перекрыт.

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;

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

Свойства

Свойства - это специальный механизм классов, регулирующий доступ к полям. Свойства объявляются с помощью зарезервированных слов property, read и write (слова read и write считаются зарезервированными только в контексте объявления свойства). Обычно свойство связано с некоторым полем и указывает те методы класса, которые должны использоваться при записи в это поле или при чтении из него. Например:

type

TaClass = class

  IntField:Integer;

  function GetField: Integer;

  procedure SetField(Value: Integer);

  Property IntegerValue: Integer read GetField write SetField;

end;

В контексте программы свойство ведет себя как обычное поле. Например, мы могли бы написать такие операторы:

var

 aClass: TaClass;

 Value: Integer;

begin

 aClass := TaClass.Create; {Обязательное обращение к конструктору

        перед обращением к полю или свойству!}

 aClass.IntegerValue := 0;

 Value := aClass.IntegerValue;

 aClass.Destroy; // Удаление ненужного объекта

end;

Более того, возможен и такой оператор присваивания:

aClass.IntField := NewValue;

Разница между этим оператором и оператором

aClass.IntegerValue := NewValue;

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

Пример: рассмотрим следующий оператор

IbOutput.Caption := 'Строка';

Свойство Caption компонента Label вызывает метод SetText, который не только запоминает строку символов во внутренней переменной, но и осуществляет прорисовку метки с новым текстом.

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

type

 TaClass = class

   IntField: Integer;

   procedure SetField(Value: Integer);

   Property IntegerValue: Integer read IntFiled write SetField;

 end;

Если необходимо, чтобы поле было доступно только для чтения или только для записи, следует опустить соответственно часть write или read. Вообще говоря, свойство может и не связываться с полем. Фактически оно описывает один или два метода, которые осуществляют некоторые действия над данными того же типа, что и свойство.

ОБЪЯВЛЕНИЕ КЛАССА

Любой вновь создаваемый класс содержит четыре секции, определяемые зарезервированными словами published (декларированные), private (личные), protected (защищенные) и public (доступные). Внутри каждой секции вначале определяются поля, а затем - методы и свойства.

Секции определяют области видимости элементов описания класса. Секция public не накладывает ограничений на область видимости перечисляемых в ней полей, методов и свойств - их можно вызывать в любом другом модуле программы. Секция published так же не ограничивает область видимости, однако в ней перечисляются свойства, которые должны быть доступны не только на этапе исполнения, но и на этапе конструирования программы (т.е. в окне Инспектора Объектов). Секция published используется только при разработке компонентов. Замечу, что по умолчанию (без объявления) секция считается объявленной как published. Такая умалчиваемая секция располагается в самом начале объявления класса любой формы и продолжается до первой объявленной секции. В нее среда Delphi помещает описания вставленных в форму компонентов. Программисту не следует помещать в нее собственные элементы описания класса или удалять из нее элементы, вставленные средой. Секция private сужает область видимости до минимума: личные элементы описания доступны только внутри методов данного класса и подпрограммах, находящихся в том же модуле, где описан класс. Элемент, объявленный в секции private, становится недоступным даже ближайшим потомкам класса, если они размещаются в других модулях. Наконец, секция protected доступна только методам самого класса, а также любым его потомкам, независимо от того, находятся ли они в том же модуле, или нет. В Object Pascal разрешается сколько угодно раз объявлять любую секцию, причем порядок следования секций не имеет значения. Любая секция может быть пустой.

Следующий фрагмент кода поясняет области видимости.

Unit Unit1;

Interface

Uses Controls, Forms;

type

TForml = class(TForm)

  Buttonl: TButton; // Эта секция обслуживается Delphi

                  // Ее элементы доступны всем

private // Эта секция доступна в модуле Unit1

  FIntField: Integer;

  procedure SetValue(Value: Integer);

  function GetValue: Integer;

published // Эта секция доступна в любом модуле

  Property IntField: read GetValue write SetValue;

protected // Эта секция доступна классам-потомкам

  procedure Proc1;

public // Эта секция доступна в любом модуле

  procedure Proc2;

end;

var

 Form1: TForm1;

Implementation

 procedure TForm1.Proc1;

 Buttonl.Color := clBtnFace; // Так можно

 FIntField := 0;             // Так можно

 IntField := 0;              // Так можно

 Proc1;                      // Так можно

 Рrос2;                      // Так можно

end;

Initialization

 Form1.Button1.Color := clBtnFace; // Так можно

 Form1.FIntField := 0;    // Так можно

 Form1.IntField := 0;    // Так можно

 Form1.Proc1;      // Так нельзя!

 Form1.Proc2;      // Так можно

end.

Unit Unit2;

Interface

Uses Controls, Unitl;

type

 TForm2 = class(TForm1)

   Button2: TButton;

   procedure Button2Click(Sender: TObject);

 end;

var

 Form2: TForm2;

Implementation

 procedure TForm2.Button2Click(Sender: ТObject);

begin

 Button1.Color := clBtnFace;  // Так можно

 FIntField := 0;    // Так нельзя!

 IntField := 0;    // Так можно

 Proc1;      // Так можно

 Ргос2;      // Так можно

end;

Initialization

 Form1.Button1.Color := clBtnFace; // Так можно

 Form1.FIntField := 0;    // Так нельзя!

 Form1.IntField := 0;    // Так можно

 Form1.Proc1;      // Так нельзя!

 Form1.Proc2;      // Так можно

end.

При объявлении класса-потомка разрешается перемещать элементы класса из одной области видимости в другую. Для предыдущего примера допустимо такое объявление:

type

 TForm2 = class(TForm1)

Public

 procedure Proc1;

end;

После этого в модуле Unit2 возможно такое обращение:

Form2.Proc1;

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

Класс может объявляться только в интерфейсной части модуля или в самом начале области реализации. Нельзя определять классы в разделе описаний подпрограмм.


 

А также другие работы, которые могут Вас заинтересовать

73613. Перевірка та регулювання теплових зазорів в газорозподільчому механізмі 74 KB
  При виконані лабораторної роботи було виявлено збільшення теплових зазорів в ГРМ двигуна. Несправність усувається регулюванням зазору. Після усунення несправності двигун допускається до експлуатації.
73614. Загальне діагностування систем охолодження і мащення на двигуні 58.5 KB
  Перевірити рівень охолоджуючої рідини в радіаторі. Перевірити технічний стан пароповітряного клапана пробки радіатора. Перевірити легкість переміщення клапана. Перевірити візуально герметичність системи охолодження.
73615. Технічне обслуговування елементів системи охолодження 70 KB
  Огляд радіатора і перевірка його герметичності. Матеріальнотехнічне оснащення робочого місця: пристрій для перевірки герметичності радіатора; компресор з ресивером або насос; пристрій для перевірки справності термостата; градусник; нагрівальний елемент; штангенциркуль; набір інструментів.
73616. Технічне обслуговування елементів системи мащення 65.5 KB
  Технічне обслуговування масляних фільтрів Очищення центрифуги відцентрового фільтра двигун ЗИЛ130 Відкрутити гайку кріплення кожуха фільтра і зняти кожух. Вигвинтити пробку з корпуса фільтра і вставити в отвір стержень який утримує корпус від провертання. Заміна фільтруючих елементів масляного фільтра двигун КАМАЗ740 Викрутити зливні пробки з ковпаків і злити масло в підготовлену місткість.