Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ООП / ООП_ЛабРаб_Методичка

.pdf
Скачиваний:
37
Добавлен:
08.06.2015
Размер:
770.97 Кб
Скачать

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

Методы

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

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.

Соседние файлы в папке ООП