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

Часть 2-Основы программирования (Delphi)

.pdf
Скачиваний:
65
Добавлен:
09.06.2015
Размер:
1.93 Mб
Скачать

TElem=record inf:real; link:TPtr; end;

var BegL,EndL:TPtr; //переменные-указатели на начало и конец списка

procedure Initial; //установка начальных значений указателей procedure InsEnd(val:real); //вставка элемента в конец списка

procedure DelBeg(var val:real); //удаление элемента из начала списка

implementation

procedure Initial; //установка начальных значений указателей begin

BegL:=Nil;

EndL:=Nil; end;

procedure InsEnd(val:real); //вставка элемента в конец списка var P:TPtr;

begin new(P); P^.inf:=val;

P^.link:=Nil;

if EndL=Nil then BegL:=P //создание списка (вставка 1-го эл-та)

else EndL^.link:=P; //вставка эл-та, не являющегося 1-ым

EndL:=P; end;

procedure DelBeg(var val:real); //удаление элемента из начала списка

var P:TPtr;

begin val:=BegL^.inf; P:=BegL; BegL:=P^.link;

if BegL=Nil then EndL:=Nil; //удаление последнего элемента списка

dispose(P); end;

end.

Глава . КЛАССЫ

Основные понятия

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

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

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

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

Класс представляет собой единство трех сущностей – полей, методов и свойств. Объединение этих сущностей в единое целое и называется инкапсуляцией. Инкапсуляция позволяет во многом изолировать класс от остальных частей программы, сделать его “самодостаточным” для решения конкретной задачи. В результате класс всегда несет в себе некоторую функциональность.

Например, класс TForm содержит (инкапсулирует в себе) все необходимое для создания Windows-окна, класс TMеmо представляет собой полнофункциональный текстовый редактор, класс TTimer обеспечивает работу программы с таймером и т. д.

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

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

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

TChildClass = class (TParentClass)

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

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

класс, который не был бы дочерним классом TObject. Следующие два объявления идентичны:

TaClass = class(TObject) TaClass = class

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

Например, класс TPersistent обогащает возможности своего родителя TObject: он “умеет” сохранять данные в файле и получать их из него, в результате это умеют делать и все его потомки. Класс TComponent (потомок TPersistent), от которого порождены все компоненты Delphi, в свою очередь, умеет взаимодействовать со средой разработчика и передает это умение своим потомкам. Данный класс включает свойства и методы, общие для всех компонентов. Класс TControl (потомок TComponent) не только способен работать с файлами и средой разработчика, но он еще умеет создавать и обслуживать видимые на экране изображения, а его потомок класс TWinControl может создавать Windows-окна и т. д. Продолжая эту цепочку (одну из ветвей дерева классов Object Pascal, порожденных от корня TObject), придем, например, к классу TForm.

Полиморфизм

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

В Object Pascal полиморфизм достигается не только описанным выше механизмом наследования и перекрытия методов родителя, но и их

виртуализацией (см. далее).

Объявление класса

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

Описание типа класса задается следующим образом:

type <имя_типа> = class (<имя_класса-родителя>) <список полей, методов, свойств>

public

<список полей, методов, свойств>

published

<список полей, методов, свойств> private

<список полей, методов, свойств> protected

<список полей, методов, свойств>

end;

Здесь <имя_типа>, <имя_класса-родителя> – правильные идентификаторы; class, public, published, private, protected, end –

зарезервированные слова (класс, доступные, опубликованные, закрытые, защищенные, конец); <список полей, методов, свойств> – список описаний соответствующих составляющих класса – полей, методов и свойств (см. далее).

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

Секции определяют области видимости элементов описания класса. Секция public не накладывает ограничений на область видимости

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

Секция published также не ограничивает область видимости, однако в ней перечисляются свойства, которые должны быть доступны не только на этапе исполнения, но и на этапе конструирования программы (т. е. в окне Инспектора объектов). Секция published используется только при разработке нестандартных компонентов. Среда Delphi помещает описания компонентов, вставленных в форму, в специальную секцию без названия, которая располагается сразу за заголовком класса и продолжается до первой объявленной секции. Эта секция – published. Программисту не следует помещать в нее собственные элементы описания класса или удалять из нее элементы, вставленные средой Delphi.

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

Секция protected доступна только методам самого класса, а также любым его потомкам, независимо от того, находятся ли они в том же модуле или нет.

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

Вкачестве примера описания типа класса приведем описание класса формы TForm1 с уже вставленными в нее компонентами метка, однострочное редактируемое текстовое поле и кнопка. По используемому в Delphi соглашению все имена классов начинаются с буквы Т.

type

TForm1 = class(TForm)

Label1: TLabel;

Edit1: TEdit; Button1: TButton;

procedure Button1Click(Sender: TObject); private

{PRIVATE DECLARATIONS }

public

{PUBLIC DECLARATIONS }

end;

Стандартный класс TForm реализует все нужное для создания и функционирования пустого Windows-окна. Класс TForm1 является наследником класса TForm, о чем свидетельствует строка

TForm1 = class(TForm)

После описания типа класса в программе описывается переменная Form1 – объект соответствующего класса TForm1:

var

Form1: TForm1;

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

1. Поля

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

type TMyClass = class

FIntField: Integer;

FStrField: String;

FObjField: TObject; end;

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

type

TMyClass = class FIntField: Integer; FStrField: String; end;

var

aObject: TMyClass; begin

...

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

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

2. Методы

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

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;

// Уничтожаем ненужный объект

MyObject.Free; // или MyObject.Destroy; end;

Вбазовом классе TObject определен метод Free, который сначала проверяет действительность адреса объекта и лишь затем вызывает деструктор Destroy. Обращение к деструктору объекта будет ошибочным, если объект не создан конструктором, поэтому для уничтожения ненужного объекта следует вызывать метод Free, как это сделано в предыдущем примере.

3. Свойства

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

type

TaClass = class IntField: Integer;

Function GetField: Integer;

Procedure SetField;

Property IntegerValue: Integer read GetField write SetField;

end;

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

var

aClass: TaClass; begin

{Обязательное обращение к конструктору перед обращением к свойству}

aClass := TaClass.Create; aClass.IntegerValue := 0;

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

end;

Более того, возможен и такой оператор присваивания: aClass.IntField := 0;

Разница между этим оператором и оператором aClass.IntegerValue := 0;

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

Вспомним использовавшийся в примерах оператор

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

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

Если необходимо, чтобы свойство было доступно только для чтения или только для записи, следует опустить соответственно часть write или read.

Некоторые особенности описания методов класса

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

type

TParentClass = class Procedure DoWork; end;

TChildClass = class(TParentClass)

Procedure DoWork; end;

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

В Object Pascal гораздо чаще используется динамическое замещение методов на этапе прогона программы. Для реализации этого метод, замещаемый в родительском классе, должен объявляться как виртуальный (virtual). В классе-потомке замещающий метод объявляется с директивой override (перекрыть). На этапе прогона программы при каждом обращении к замещаемому методу класса-родителя компилятор осуществляет обращение к коду метода класса-потомка, что позволит родителю выполнить нужное действие с помощью нового метода.

Пусть, например, родительский класс с помощью методов Show и Hide соответственно показывает что-то на экране или прячет изображение. Для создания изображения он использует метод Draw:

type

TVisualObject = class(TObject)

Procedure Hide;

Procedure Show; Procedure Draw; virtual; end;

TVisualChildObject = class(TVisualObject)

Procedure Draw; override; end;

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

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

type

TVisualObject = class(TObject)

Procedure Draw; virtual; abstract; end;

TVisualChildObject = class(TVisualObject)

Procedure Draw; override; end;

var

aVisualObject: TVisualObject; aVisualChild: TVisualChildObject; begin

aVisualObject.Draw; {Ошибка – Обращение к абстрактному методу} aVisualChild.Draw; {Нормальное обращение. Метод DRAW перекрыт}

end;

Обращение к неперекрытому абстрактному методу вызывает ошибку периода исполнения.

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

ПРИМЕР.

Класс – прямоугольник. Методы – расчет периметра и площади прямоугольника. Поля – длины сторон прямоугольника. Привести пример на использование класса.

На форме Form1 размещаются компоненты Edit1 и Edit2 – для ввода длин сторон прямоугольника, Edit3 и Edit4 – для вывода значений периметра и площади прямоугольника, Button1 – кнопка выполнения вычислений.

Далее приводится текст модуля Unit1, связанного с формой Form1. unit Unit1;

interface uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type

TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit;

Button1: TButton; Edit3: TEdit; Edit4: TEdit; Label1: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel;

procedure Button1Click(Sender: TObject); private

{Private declarations } public

{Public declarations } end;

TRectangle

= class

//класс-прямоугольник

a: real;

//поле - 1-ая сторона прямоугольника

b: real;

//поле - 2-ая сторона прямоугольника

function

perimeter: real; //метод - периметр прямоугольника

function square: real;

//метод - площадь прямоугольника

end;

 

 

var

Form1: TForm1;

p: TRectangle; //объект-прямоугольник

implementation

{$R *.dfm}

function TRectangle.perimeter: real; //реализация метода - периметр begin

result := 2*(a+b); end;

function TRectangle.square: real; //реализация метода - площадь begin

result := a*b; end;

procedure TForm1.Button1Click(Sender: TObject); begin

p := TRectangle.Create; //создание объекта

//действия с созданным объектом

p.a := strtofloat(Edit1.Text); //задание 1-ой стороны прямоугольника p.b := strtofloat(Edit2.Text); //задание 2-ой стороны прямоугольника Edit3.Text := floattostr(p.perimeter); //вычисление периметра