Delphi работа с классами

Вот пример написания класса. Этот класс вычисляет сумму квадратов введенных чисел. Этот класс написан мной только для примера, и я исходил из воображений наглядности, а не оптимальности. Большая часть реализации не только не оптимальна, но и бессмыслена, но показывает бОльшую часть простейших приемов создания класса.

Вот пример использования этого класса:

Copyright © 2004-2020 "Delphi Sources". Delphi World FAQ

Добрый день, Мастера! У меня довольно банальный вопрос по классированию в Delphi, а именно по работе с представителями класса.
Вообще говоря, этот материал я намного лучше помню по C++, а классирование в Turbo Pascal"е прошло мимо меня 🙂
Так вот, если не сложно – в двух словах: замечания по нижележащему фрагменту (пример объявления класса, плюс конструктор и функция которая не видит представителя класса, передоваемого по ссылке) и (если Вас не затруднит – какие-то рассуждения по вопросу в целом.

TCellLetter = class(TObject)
Letter:char;
CellUp:PCellLetter;
CellDown:PCellLetter;
CellRight:PCellLetter;
CellLeft:PCellLetter;
.
constructor Create; overload;
destructor Destroy;
.
procedure SetCoord(Init_x,Init_y:integer);
protected
x,y:integer;
end;
.

constructor TCellLetter.Create;
begin
Letter:=char(0);
CellUp:=nil;
CellDown:=nil;
CellRight:=nil;
CellLeft:=nil;
x:=-1;
y:=-1;
end;

procedure TCellLetter.SetCoord(Init_x,Init_y:integer);
begin
if (x 4) or (y 4) then Exit;
x:=Init_x;
y:=Init_y;
end;

//Вот здесь-то и нужны пояснения:
//либо я неправильно создаю объект, либо нельзя таким
//образом присваивать указатели. Насколько я
//помню из C++, там надо было перегружать оператор
//присваивания, создавать второй экземпляр и копировать поля.
//посоветуйте, что делать здесь – при вызове функции
//SetCoord происходит ошибка в месте где присваиваются
//x и y.
var PCell:PCellLetter;
.
PCell:=Pointer(TCellLetter.Create(Letter,Move));
if (Head=nil) then
begin
Head:=PCell;
Head.SetCoord(x,y);
Result:=Head;
Exit;
end;

Посоветуйте – где найти какой-нибудь классический пример на работу с классами в Delphi – с динамическим выделением памяти, передачей педставителя класса в функцию, работой со ссылками и т.д.


DieHard ( 2002-03-06 10:15 ) [1]

В Delphi объекты создаются так:

var
Cell:TCellLetter;
begin
.
Cell:=TCellLetter.Create(Letter,Move);
.

Cell по существу и является указателем, так что никаких дополнительных действий делать не нужно


nebula ( 2002-03-06 10:18 ) [2]

Занесло тебя однако.
PCellLetter = ^. – совсем не нужно.
CellXXX: TCellLetter
CellXXX := TCellLetter.Create
для предварительного описания класса просто пиши
TCellLetter = class;
Перегрузки операторов в Pascal-е нет.
constructor Create; – overeload лишний.
и т.д и т.п. Почитай про Object Pascal. Пожалуйста.


Виктор Щербаков ( 2002-03-06 10:22 ) [3]

Экземпляры классов в Object Pascal создаются динамически. По-этому переменные классового типа являются указателями. В стеке можно создавать только объекты (эта модель оставлена для совместимости со старыми вепсиями Object Pascal). Смотри в хэлпе: object types. Но Borland не рекоммендует этим пользоваться.


dimmu ( 2002-03-08 17:57 ) [4]

Ага, а как с передачей представителя класса в функцию/процедуру? Здесь есть какие-нибудь хитрости?


drpass ( 2002-03-08 19:28 ) [5]

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

Так должно работать
var Cell:TCellLetter;
.
Cell:=TCellLetter.Create(Letter,Move);
if not Assigned(Head) then
begin
Head:=Cell;
Head.SetCoord(x,y);
Result:=Head;
Exit;
end;


dimmu ( 2002-03-09 00:10 ) [6]

Это ясно. Вот еще вопрос: после присваивания

Head:=Cell;

ссылка Cell будет указывать на тот же объект, на который ссылался Head, и если будет вызвано что-то вроде

dispose(Cell);//при выходе из функции память под все
//локальные переменные будет высвобождаться примерно таким
//образом – при автоматическом вызыве деструктора.

Читайте также:  Телевизор samsung 82 диагональ

то Head будет ссылаться на что-то очень сомнительное (nil в лучшем случае).
Насколько я помню в С++ с этим боролись перегрузкой "="(как я уже писал выше), а как поступают в Delphi?


Dimk ( 2002-03-09 00:38 ) [7]

Head-у присвоится указатель на тот же объект.
Говоря на C++ кусок кода

var
Head, Cell: TCellLetter;
begin
Cell := TCellLetter.Create;
Head := Cell;
end;

<
TCellLetter * Head, Cell; // в Паскале переменная объектного типа – указатель ( хотя явно это не видно а написано в доке . )

Cell = new TCellLetter();
Head = Cell;
>


drpass ( 2002-03-09 00:45 ) [8]

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

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

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

В любом вашем Делфи-приложении вы можете увидеть строки:

Это объявление класса TForml вашей формы и объявление переменной Forml -объекта этого класса.

В общем случае синтаксис объявления класса следующий:

Имя класса может быть любым допустимым идентификатором. Но принято идентификаторы большинства классов начинать с символа "Т". Имя класса – родителя может не указываться. Тогда предполагается, что данный класс является непосредственным наследником TObject – наиболее общего из предопределенных классов. Таким образом, эквивалентны следующие объявления: В приведенном ранее объявлении класса формы TForml видно, что его родительским классом является класс TForm.

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

Раздел public (открытый) предназначен для объявлений, которые доступны для внешнего использования. Это открытый интерфейс класса. Раздел published (публикуемый) содержит открытые свойства, которые появляются в процессе проектирования на странице свойств Инспектора Объектов и которые, следовательно, пользователь может устанавливать в процессе проектирования. Раздел private (закрытый), содержит объявления полей, процедур и функций, используемых только внутри данного класса. Раздел protected (защищенный) содержит объявления, доступные только для потомков объявляемого класса. Как и в случае закрытых элементов, можно скрыть детали реализации защищенных элементов от конечного пользователя. Однако в отличие от закрытых, защищенные элементы остаются доступны для программистов, которые захотят производить от этого класса производные объекты, причем не требуется, чтобы производные объекты объявлялись в этом же модуле.

Объявления полей выглядят так же, как объявления переменных или объявления полей в записях:

В приведенном ранее объявлении класса формы вы можете видеть строку

Это объявление объекта (поля) Buttonl типа (класса) TButton.

Имеется одно очень существенное отличие объявления поля от обычного объявления переменной: в объявлении поля не разрешается его инициализация каким-то значением. Автоматически проводится стандартная инициализация: порядковым типам в качестве начального значения задается 0, указателям — nil, строки задаются пустыми. При необходимости задания других начальных значений используются конструкторы, описанные далее в разд. 3.5.3.

Объявления методов в простейшем случае также не отличаются от обычных объявлений процедур и функций.

3.5.2 Свойства

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

Читайте также:  Майкрософт офис визио 2007

Свойство объявляется оператором вида:

Если в разделах read или write этого объявления записано имя поля, значит предполагается прямое чтение или запись данных (т.е. обмен данными непосредственно с полем).

Если в разделе read записано имя метода чтения, то чтение будет осуществляться только функцией с этим именем. Функция чтения — это функция без параметра, возвращающее значение того типа, который объявлен для свойства. Имя функции чтения принято начинать с префикса Get, после которого следует имя свойства.

Если в разделе write записано имя метода записи, то запись будет осуществляться только процедурой с этим именем. Процедура записи — это процедура с одним параметром того типа, который объявлен для свойства. Имя процедуры записи принято начинать с префикса Set, после которого следует имя свойства.

Если раздел write отсутствует в объявлении свойства, значит это свойство только для чтения и пользователь не может задавать его значение.

Директивы запоминания определяют, как надо сохранять значения свойств при сохранении пользователем файла формы .dfm. Чаще всего используется директива default – значение по умолчанию.
Она не задает начальные условия. Это дело конструктора. Директива просто говорит, что если пользователь в процессе проектирования не изменил значение свойства но умолчанию, то сохранять значение свойства не надо.

Приведем пример. Мы уже не раз использовали объекты, содержащие сведения о сотруднике некоторой организации. Но раньше мы реализовывали эти объекты в виде записей (см. разд. 3.3). А теперь рассмотрим более общий подход — реализацию класса подобных объектов.

Начните новый проект. Объявление класса, который мы хотим создать, можно поместить непосредственно в файл модуля. Но если вы хотите создать класс, который будете использовать в различных проектах, лучше оформить его в виде отдельного модуля unit, сохранить в каталоге своей библиотеки или библиотеки Delphi, и подключать в дальнейшем к различным проектам с помощью предложения uses. Если вы забыли, как создается отдельный модуль, не связанный с формой, и как он сохраняется в библиотеке, посмотрите все это в разд. 2.8.7.4. Мы выберем именно этот вариант. Так что выполните команду File | New | Unit (в некоторых более старых версиях Delphi — File | New и на странице New выберите пиктограмму Unit). Сохраните сразу этот ваш модуль в библиотеке под именем, например, MyClasses. А в модуль формы введите оператор, ссылающийся на этот модуль:

Теперь займемся созданием класса в модуле My > Вглядимся в приведенный код. Интерфейсный раздел модуля interface начинается с предложения uses. Заранее включать это предложение в модуль не требуется. Но по мере написания кода вы будете встречаться с сообщениями компилятора о неизвестных ему идентификаторах функций, типов и т.п. Столкнувшись с таким сообщением, надо посмотреть во встроенной справке Delphi или в справке [3], в каком модуле объявлена соответствующая функция или класс. И включить этот модуль в приложение uses.

Теперь обратимся к объявлению класса. Объявленный класс TPerson наследует непосредственно классу TObject, поскольку родительский класс не указан. В закрытом разделе класса private объявлен ряд полей. Поле FName предполагается использовать для фамилии, имени и отчества человека. Поля FDepl, FDep2, FDep3 будут использоваться под указание места работы или учебы. Поле FYear будет хранить год рождения, поле FSex – указание пола: символ "м" или "ж". Поле FAttr будет хранить какую-то характеристику: штатный — нештатный, отличник или нет и т.п. Поле FComment предназначено для каких-то текстовых комментариев. В частности, в нем можно хранить свойство Text многострочного окна редактирования Memo или RichEdit. Так что это может быть развернутая характеристика человека, правда, без форматирования.

Читайте также:  4 Мбит с это быстро или нет

В открытом разделе класса public объявлены свойства, соответствующие всем полям. Чтение всех свойств осуществляется непосредственно из полей. Запись во всех свойствах, кроме Sex, осуществляется тоже непосредственно в поля. А для поля Sex указана в объявлении свойства процедура записи SetSex, поскольку надо следить, чтобы по ошибке в это поле не записали символ, отличный от "м" и "ж". Соответственно в защищенном разделе класса protected содержится объявление этой процедуры. Как говорилось ранее, она должна принимать единственный параметр типа, совпадающего с типом свойства.

В раздел модуля implementation введена реализация процедуры записи SetSex. Ее заголовок повторяет объявление, но перед именем процедуры вводится ссылка на класс TPerson, к которому она относится. Не забывайте давать такие ссылки для методов класса. Иначе получите сообщение компилятора об ошибке; Unsatisfied forward or external declaration: TPerson.SetSex — нереализованная ранее объявленная или внешняя функция TPerson.SetSex.

Тело процедуры SetSex в особых комментариях не нуждается. В нем проверяется допустимость символа, переданного в процедуру через параметр Value. Если символ допустимый, то его значение заносится в поле FSex. Это поле, как и другие закрытые поля, открыто для методов данного класса. При ошибочном символе пользователю выдается сообщение об ошибке. Правда, лучше было бы в этом случае сгенерировать исключение, но пока мы не обсуждали, как это можно делать.

Вы создали класс в вашем модуле MyClasses. Давайте посмотрим, как можно использовать объекты нашего класса. Создайте в модуле формы Unit1 вашего приложения тест класса TPerson. Введите в модуль операторы:

uses Classl;
var Pers: TPerson;

Они обеспечивают связь с модулем, описывающим класс, и объявляют переменную Pers, через которую вы будете связываться с объектом класса. Но так же, как при работе с другими объектами и записями, объявление этой переменной еще не создает сам объект. Это указатель на объект, и его значение равно nil.

Создание объекта вашего класса TPerson должно осуществляться вызовом его конструктора Create. Так что создайте обработчик события OnCreate вашей формы, и вставьте в него оператор:

Вот теперь объект создан, и переменная Pers указывает на него. Чтобы не забыть очистить память от этого объекта при завершении работы приложения, сразу создайте обработчик события OnDestroy формы, и вставьте в него оператор:

Почему ваш объект воспринимает методы Create и Free? Ведь вы их не объявляли в классе TPerson! Это работает механизм наследования. В классе TObject, являющемся родительским для TPerson, методы Create и Free имеются. А поскольку вы их не перегружали, то ваш класс наследует их.

Теперь перенесите на форму четыре окна Edit, окно Memo и две кнопки. Пусть первая кнопка с надписью Запись заносит в объект Pers данные из окон редактирования: ‘ фамилию с именем и отчеством, пол, подразделение, в котором работает или обучается человек, год рождения, характеристику из окна Memo. Обработчик щелчка на ней может иметь вид:

А вторая кнопка с надписью Чтение пусть осуществляет чтение информации из объекта в окна редактирования. Обработчик щелчка на ней может иметь вид:

Выполните ваше приложение. Занесите в окна редактирования какую-то подходящую информацию и щелкните на кнопке Запись. А потом сотрите тексты всех окон редактирования и щелкните на кнопке Чтение. Информация в окнах должна восстановиться. Проверьте также реакцию на неверный символ, задающий пол.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock detector