
ООП / ООП_ЛабРаб_Методичка
.pdfдереве иерархии располагается класс, тем больше данных получают в свое распоряжение его объекты.
Методы
Инкапсулированные в классе процедуры и функции называются методами. Они объявляются так же, как и обычные подпрограммы:
type
TMyClass = class
Function MyFunc(aPar: Integer): Integer;
Procedure MyProc;
end;
Доступ к методам класса, как и к его полям, возможен с помощью составных имен:
var
aObject: TMyClass; begin
aObject.MyProc;
end;
В состав любого класса входят два специальных метода - конструктор и деструктор. У класса 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;
Свойства
Свойства - это специальный механизм классов, регулирующий доступ к полям. Свойства объявляются с помощью зарезервированных слов 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, в котором могут реализовываться специфичные действия.
Если нет необходимости в специальных действиях при чтении или записи свойства, вместо имени соответствующего метода можно указывать имя поля:
type
TaClass = class
IntFiled: Integer;
Procedure SetField(Value: Integer);
Property IntegerValue: Integer read IntFiled write SetField;
end;
Если необходимо, чтобы поле было доступно только для чтения или только для записи, следует опустить соответственно часть write или read. Вообще говоря, свойство может и не связываться с полем. Фактически оно описывает один или два метода, которые осуществляют некоторые действия над данными того же типа, что и свойство.
Объявление класса
Любой вновь создаваемый класс может содержать секции (разделы), определяемые зарезервированными словами published (декларированные), private (личные), protected (защищенные), public (доступные) и automated (автоматизированные). Внутри каждой секции вначале определяются поля, а затем - методы и свойства.
Секции определяют области видимости элементов описания класса. Секция public не накладывает ограничений на область видимости перечисляемых в ней полей, методов и свойств - их можно вызывать в любом другом модуле программы. Секция published также не ограничивает область видимости, однако в ней перечисляются свойства, которые должны быть доступны не только на этапе исполнения, но и на этапе конструирования программы (т.е. в окне Инспектора Объектов). Секция published используется только при разработке нестандартных компонентов. Среда Delphi помещает описания компонентов, вставленных в форму, в специальную секцию без названия, которая располагается сразу за заголовком класса и продолжается до первой объявленной секции. Эта секция - published.
Программисту не следует помещать в нее собственные элементы описания класса или удалять из нее элементы, вставленные средой. Секция private сужает область видимости до минимума: личные элементы описания доступны только внутри методов данного класса и в подпрограммах, находящихся в том же модуле, где описан класс. Элемент, объ- явленный в секции private, становится недоступным даже ближайшим потомкам класса, если они размещаются в других модулях. Секция protected доступна только методам самого класса, а также любым его потомкам независимо от того, находятся ли они в том же модуле или нет. Наконец, секция automated используется только для объявления свойств и методов, которые будут добавлены к так называемому интерфейсу OLE- объектов автоматизации; область видимости членов этой секции не ограничена.
В Object Pascal разрешается сколько угодно раз объявлять любую секцию, причем порядок следования секций не имеет значения. Любая секция может быть пустой. Следующий фрагмент кода поясняет области видимости.
Unit Unit1;
Interface
Uses Controls, Forms; type
TForm1 = class(TForm)
Button1: TButton; // Эта секция обслуживается Delphi // Ее элементы доступны всем
private // Эта секция доступна в модуле Unit1
FIntField: Integer;
Procedure SetValue(Value: Integer); Function GetValue: Integer;
published // Эта секция доступна в любом модуле
Property IntField: read GetValue write SetValue;
protected // Эта секция доступна классам-потомкам
Procedure Proс1;
public // Эта секция доступна в любом модуле
Procedure Proc2;
end; |
|
var |
|
Forml: TForml; |
|
Implementation |
|
Procedure TForml.Procl; |
|
begin |
// Так можно |
Buttonl.Color := clBtnFace; |
|
FIntField := 0; |
// Так можно |
IntField := 0; |
// Так можно |
Procl; |
// Так можно |
Proc2; |
// Так можно |
end; |
|
begin |
// Так можно |
Forml.Buttonl.Color := clBtnFace; |
|
Forml.FIntField := 0; |
// Так можно |
Forml.IntField := 0; |
// Так можно |
Forml.Procl; |
// Так нельзя |
Forml.Proc2; |
// Так можно |
end. |
|
Unit Unit2;
Interface
Uses Controls, Unitl; type
TForm2 = class(TForml) Button2: TButton;
Procedure Button2Click(Sender: TObject);
end; var
Form2: TForm2;
Implementation
Procedure TForm2.Button2Click(Sender: TObject); begin
Button1.Color:= clBtnFace; |
// Так можно |
FIntField := 0; |
// Так нельзя |
IntField := 0; |
// Так можно |
Procl; |
// Так можно |
Proc2; |
// Так можно |
end; |
|
begin |
// Так можно |
Forml.Buttonl.Color := clBtnFace; |
|
Forml.FIntField := 0; |
// Так нельзя |
Forml.IntField := 0; |
// Так можно |
Forml.Procl; |
// Так нельзя |
Forml.Proc2; |
// Так можно |
end. |
|
При объявлении класса-потомка разрешается перемещать элементы класса из одной области видимости в другую. Для предыдущего примера допустимо такое объявление:
type
TForm2 = class(TForml) …..
Public
Procedure Proc1;
……
end;
После этого в модуле Unit2 возможно такое обращение:
Form2 . Proс1;
После перемещения в секцию private элемент объявления становится невидим потомкам (если потомок, как это обычно бывает, объявляется в другом модуле) и, следовательно, его уже нельзя переместить в другую секцию.
Класс может объявляться только в интерфейсной области модуля или в самом начале области реализации. Нельзя определять классы в разделе описаний подпрограмм.
3.Методика выполнения
3.1.Создайте в Delphi новый проект и сохраните его в отдельном каталоге.
3.2.Разработайте интерфейс пользователя приложения – разместите на форме компонент для отображения списка, компоненты для отображения значений полей выбранного в списке объекта, кнопки для операций ввода, удаления, редактирования объектов в списке. Для операций ввода и редактирования предусмотрите кнопки «Сохранить» и «Отменить».
3.3.В отдельном модуле объявите класс, описывающий объекты заданной предметной области. Определите в классе поля, методы, свойства, конструктор и деструктор. Запретите непосредственный доступ к полям данных класса.
3.4.Подключите модуль к приложению.
3.5.Объявите в модуле формы в разделе Private поле данных с типом динамический массив объектов созданного класса.
3.6.Разработайте процедуру обработки события OnClick для кнопки ввода данных об объекте, в которой сделайте активными кнопки «Сохранить» и «Отменить» и неактивными все другие.
3.7.Разработайте процедуру обработки события OnClick для кнопки «Сохранить», в которой создать объект выбранного класса, и поместите ссылку на него в динамический массив объектов; имя и тип объекта добавьте в компонент отображения; сделайте кнопки «Сохранить» и «Отменить» неактивными, а все другие - активными.
3.8.Разработайте процедуру обработки события OnClick для кнопки «Отменить», в которой сделать кнопки «Сохранить» и «Отменить» неактивными, а все другие - активными.
3.9.Разработайте процедуру обработки события OnClick для компонента отображения, в которой выведите данные о выбранном объекте в компоненты для отображения значений полей.
3.10.Разработайте процедуру обработки события OnClick для кнопки редактирования данных об объекте, в которой сделайте активными кнопки «Сохранить» и
«Отменить» и неактивными все другие. Добавьте в обработчик события OnClick кнопки «Сохранить» проверку операции (ввод или редактирование) и в случае редактирования не создавайте новый объект, а изменяйте значения свойств текущего объекта списка.
3.11.Разработайте процедуру обработки события OnClick для кнопки удаление данных об объекте, в которой перед удалением запрашивайте подтверждение операции с помощью функции MessageDlg. Описание функции и примеры использования найдите в справочнй системе Delphi.
4.Содержание отчета
Отчет готовится один на бригаду в рукописном или печатном виде и должен содержать следующие разделы:
-задание;
-листинг программы с подробными комментариями;
-копию экранной формы с реальным списком объектов заданной предметной области.
5.Контрольные вопросы
5.1.Расскажите об устройстве компонента TListView и его основных свойствах и методах.
5.2.Объясните, что в языке Object Pascal означают понятия класс, объект и компонент?
5.2.Из чего состоит класс?
5.3.Что такое свойство класса? Как оно объявляется?
5.4.Какие функции в классе выполняют конструктор и деструктор?
5.5.Для чего предназначены секции класса?
5.6.Как в классах реализуется принцип наследования?
5.7.Какой класс является базовым для всех классов языка Object Pascal?
Лабораторная работа № 5
Наследование и полиморфизм, статические и виртуальные методы
Цель работы: знакомство с полиморфизмом позднего связывания и практическое использование виртуальных методов классов в приложениях на языке Object Pascal.
1.Задание
1.1.В соответствии с вариантом задания разработать абстрактный базовый класс для представления графических фигур и дочерние классы для представления объектов типа труегольник, прямоугольник, окружность и трапеция.
1.2.Базовый класс должен иметь поле для хранения имени фигуры и свойство для доступа к этому полю, а также абстрактные виртуальные методы для ввода и вывода координат вершин фигуры, а также для вычисления длины контура, площади и геометрического центра фигуры. Будем считать геометическим центром треугольника точку пересечения медиан, прямоугольника и трапеции – пересечение диагоналей, окружности – ее центр.
1.3.Дочерние классы должны иметь поля и свойства для представления типа фигуры и координат (X,Y) ее вершин, а также перекрытые виртуальные методы базового класса. Для окружности задавать ее центр и радиус.
1.4.Пользуясь средствами Borland Delphi, разработать Windows-приложение c графическим интерфейсом пользователя (окно Windows) для выполнения следующих операций с объектами:
∙ввод данных о геометрических фигурах заданных типов;
∙просмотр данных об объектах в виде списка TListView с отображением типа, наименования, строки с координатами вершин, длины контура, площади и геометрического центра фигуры;
∙динамический массив объектов в приложении не использовать. Ссылки на объекты классов в компоненте отображения хранить во внутреннем поле Items[i].Data компонента TListView;
∙редактирование выбранного объекта;
∙удаление выбранного объекта;
∙пересчет длин контуров, площадей и геометрических центров фигур;
∙сортировка заданного набора объектов по типу, наименованию, значению длины контура или площади фигуры;
∙поиск объекта по его наименованию.
1.5.Доступ к атрибутам объектов осуществлять только с помощью свойств класса.
2.Краткая теория
Перегрузка методов
При описании нового класса можно добавлять новые методы и свойства, оставляя методы и свойства родителей, а можно родительские методы и свойства переопределить или перегрузить.
Имеется четыре вида методов: статические, виртуальные, динамические и абстрактные.
По умолчанию все методы статические. Если в классе — наследнике переопределить такой метод (ввести новый метод с тем же именем), то для объектов этого
класса новый метод отменит родительский. Если обращаться к объекту этого класса, то вызываться будет новый метод. Но если обратиться к объекту как к объекту родительского класса, то вызываться будет метод родителя. Замещение выполняется компилятором при трансляции программы (полиморфизм раннего связывания).
В Object Pascal гораздо чаще используется динамическое замещение методов на этапе прогона программы (полиморфизм позднего связывания).
Виртуальные и динамические методы не связаны с другими методами с тем же именем в классах — наследниках. Если в классах — наследниках эти методы перегружены, то при обращении к такому методу во время выполнения всегда будет вызываться тот из методов с одинаковыми именами, который соответствует классу объекта, указанному при вызове (т.е. классу, конструктором которого был создан объект).
Например, если имеется базовый класс графических объектов TShape и ряд наследующих ему классов различных геометрических фигур, и если в каждом из этих классов определен свой виртуальный метод Draw, рисующий эту фигуру, то можно написать в программе:
var ShapeArray: array[1..10] of TShape;
…
for i:=l to 10 do ShapeArray[i].Draw;
В этом коде в массив ShapeArray могут помещаться объекты разных классов,
наследующих TShape. В цикле for обращение к объектам производится как к объектам базового для них типа TShape. В этом случае для каждого объекта будет вызываться виртуальный метод Draw именно этого объекта.
При объявлении в классе виртуальных и динамических методов после точки с запятой, завершающей объявление метода, добавляются ключевые слова virtual или dynamic. Например:
type
TShape = class
procedure Draw; virtual; end;
Чтобы перегрузить в классе — наследнике виртуальный метод, надо после его объявления поставит ключевое слово override. Например:
type
TRectangle = class (TShape) procedure Draw; override;
end;
TEllipse = class(TShape) procedure Draw; override; end;
Если в каком-то базовом классе метод был объявлен как виртуальный, то он остается виртуальным во всех классах — наследниках (в частности, и в наследниках классов наследников). Однако, обычно для облегчения понимания кодов перегруженные методы принято повторно объявлять виртуальными, чтобы была ясна их суть для тех, кто будет строить наследников данного класса. Например:
TEllipse = class(TShape) procedure Draw; override; virtual; end;
Различие между динамическими и виртуальными методами невелико и относится к внутреннему механизму реализации их вызовов. Виртуальные методы эффективнее с точки зрения временных затрат, а динамические — с точки зрения затрат памяти. В целом виртуальные методы обеспечивают более эффективный механизм полиморфизма, а дина- мические более выгодны, если в базовом классе определено много перегружаемых методов и они одновременно используются многими объектами классов — наследников.
Встретив объявление dynamic, компилятор создаст таблицу DMT (Dynamic Method Table) и поместит в нее адреса точек входа динамических методов класса. При объявлении virtual создается таблица VMT (Virtual Method Table), в которую помещаются адреса точек входа виртуальных методов. При каждом обращении к замещаемому методу компилятор вставляет код, позволяющий извлечь адрес точки входа в подпрограмму из той или иной таблицы. В классе-потомке замещающий метод объявляется с директивой override (перекрыть). Получив это указание, компилятор создаст код, который на этапе прогона программы поместит в родительскую таблицу точку входа метода класса-потомка, что позволит родителю выполнить нужное действие с помощью нового метода.
Разница между динамическими и виртуальными методами состоит в том, что таблица динамических методов DMT содержит адреса только тех методов, которые объявлены как dynamic в данном классе, в то время как таблица VMT содержит адреса виртуальных методов не только данного класса, но и всех его родителей. Значительно большая по размеру таблица VMT обеспечивает более быстрый поиск, в то время как при обращении к динамическому методу программа сначала просматривает таблицу DMT у объекта, затем - у его родительского класса и так далее, пока не будет найдена нужная точка входа.
Абстрактные методы и классы
Абстрактный метод — это виртуальный или динамический метод, реализация которого не определена в том классе, в котором он объявлен. Предполагается, что этот метод будет перегружен в классах — наследниках. Только в тех классах, в которых он перегружен, его и можно вызывать.
Объявляется абстрактный метод с помощью ключевого слова abstract после слова virtual или dynamic. Например:
procedure DoSomething; virtual; abstract;
Обращение к неперекрытому абстрактному методу вызывает ошибку периода исполнения. Разумеется, в грамотно составленной программе абстрактные методы никогда не вызываются. Классы, содержащие абстрактные методы, называются абстрактными. Такие классы инкапсулируют общие свойства своих неабстрактных потомков, но объекты абстрактных классов никогда не создаются и не используются. Для эксплуатации абстрактных классов в библиотеку классов Delphi включаются классы- потомки, в которых перекрываются абстрактные методы родителя.
3.Методика выполнения
3.1.Создайте в Delphi новый проект и сохранить его в отдельном каталоге.
3.2.Используя результаты работы № 4, разработайте пользовательский интерфейс приложения, содержащий компонент для отображения списка типа TListView, 2 компонента TEdit для ввода и отображения названия и координат вершин фигуры, кнопки для операций ввода, удаления, редактирования, пересчета
значений, поиска и сортировки объектов в списке. Тип фигуры выбирайте с помощью группы радиокнопок или компонента TComboBox. Для операций ввода
и редактирования предусмотрите кнопки «Сохранить» и «Отменить».
3.3.В отдельном модуле объявите базовый и дочерние классы для представления графических объектов. Определите в классах поля, методы, свойства, конструкторы и деструкторы. Запретите непосредственный доступ к полям данных класса. Рекомендуется в методе ввода координат вершин фигуры использовать параметр типа строка, в котором передавать для каждого типа фигур требуемое количество пар целых чисел (X,Y), задающих координаты вершин. Метод должен выполнить разбор строки и заполнить поля, в которых хранятся координаты вершин. В случае обнаружения ошибки в строке следует генерировать исключение оператором Raise (см. Delphi Help). Метод вывода
координат вершин должен по значениям полей класса сформировать такую строку.
3.4.Подключите модуль к приложению.
3.5.Разработайте процедуру обработки события OnClick для кнопки ввода данных об объекте, в которой сделайте активными кнопки «Сохранить» и «Отменить» и неактивными все другие.
3.6.Разработайте процедуру обработки события OnClick для кнопки «Сохранить», в которой создайте объект выбранного класса, установите значение его свойства наименование и координаты вершин; добавьте новый элемент в список типа TListView, сохраните в нем имя и тип фигуры, в поле Data поместите ссылку на созданный объект; сделайте кнопки «Сохранить» и «Отменить» неактивными, а все другие - активными.
3.7.Разработайте процедуру обработки события OnClick для кнопки «Отменить», в которой сделайте кнопки «Сохранить» и «Отменить» неактивными, а все другие - активными.
3.8.Разработайте процедуру обработки события OnClick для компонента отображения, в которой выведите данные о выбранном объекте в компоненты для отображения значений полей.
3.9.Разработайте процедуру обработки события OnClick для кнопки редактирования данных об объекте, в которой сделайте активными кнопки «Сохранить» и «Отменить» и неактивными все другие. Добавьте в обработчик события OnClick кнопки «Сохранить» проверку операции (ввод или редактирование) и в случае редактирования не создавайте новый объект, а изменяйте значения свойств текущего объекта списка.
3.10.Разработайте процедуру обработки события OnClick для кнопки удаление данных об объекте, в которой перед удалением запрашивайте подтверждение операции с помощью функции MessageDlg. Описание функции и примеры использования найдите в справочной системе Delphi.
3.11.Разработайте процедуру обработки события OnClick для кнопки пересчет значений. В цикле для каждого элемента списка фигур с помощью поля Data
необходимо получить указатель на объект класса геометрических фигур и вызвать методы для вычисления длины контура, площади и геометрического центра фигуры. Результаты поместите в соответствующие колонки списка.
3.12.Разработайте процедуру обработки события OnClick для кнопки поиска объекта в списке. Параметры поиска (и сортировки) задавайте непосредственно в главной форме приложения с помощью компонента TRadioGroup.Для задания значения поиска используйте функцию InputQuery. Описание функции и примеры использования найдите в справочнй системе Delphi.