Как определяется понятие объекта в object pascal
Перейти к содержимому

Как определяется понятие объекта в object pascal

  • автор:

Как определяется понятие объекта в object pascal

Основным понятием ООП и элементом программы является объект, сочетающий в себе как совокупность данных, так и действий над ними. Тип-объект в Turbo Pascal напоминает тип-запись, однако вместо зарезервированного слова record используется слово object, а кроме полей, представляющих данные, в нем перечислены и заголовки подпрограмм, называемых методами. При задании такого типа после зарезервированного слова object перечисляются все поля объекта и заголовки методов, после чего пишется слово end. Так, в рассматриваемом примере используется тип tConnection (связь элементов):

type
tConnection = object
PredElem: Pointer;
NextElem: Pointer;
procedure PutPredElemCPredEl: Pointer);
procedure PutNextElem(NextEl: Pointer);
function GetPredElem: Pointer;
function GetNextElem: Pointer
end;

В этом типе PredElem и NextElem — указатели на предыдущий и последующий элементы в структуре (если соответствующего элемента нет, указатель имеет значение nil). Используются указатели типа Pointer, т. к. элементы могут быть различными: они могут быть и элементами строки, и строками. Далее идут заголовки двух процедур и двух функций, позволяющих либо задавать, либо получать значения указателей объекта.

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

procedure tConnection.PutPredElem;
begin
PredElem:=PredEl
end;

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

Некоторые объекты программы, особенно находящиеся в начале иерархического дерева, могут и не соответствовать каким-либо реальным объектам. Так, например, объекты типов tConnection (связь), tStructure (структура) и tOperation (операция) не имеют какого либо физического воплощения — они указывают лишь на некоторые свойства других, реальных объектов, таких, как строки, элементы строк. Однако выделение этих общих свойств в отдельные объекты бывает удобно, т. к. позволяет затем не повторять их многократно при описании уже реальных объектов. Такие объекты называются абстрактными, и переменных таких типов в программе, как правило, не бывает.

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

Под термином «инкапсуляция» понимается совмещение в одном объекте как параметров, так и действий над ними. При этом включенные в объект подпрограммы (методы), как правило, оперируют с данными этого объекта или обращаются к методам объектов-предков (см. п. 14.2.2). Это позволяет объединить в одном месте все свойства объекта, что облегчает понимание работы программы, 1С отладку, модификацию. Так, например, все свойства связей между элементами в структуре текста сосредоточены в типе tConnection. Как правило, к данным Объекта извне непосредственно не обращаются, хотя это и возможно. Для обращения к данным обычно используют соответствующие методы. Так, в рассматриваемом примере для этой цели служат четыре метода PutPredElem, PutNextElem, (idPredElem и GetNextElem, с помощью которых можно задавать и получать значения указателей на предыдущий и последующий элемент. Это обстоятельство не является надуманным. В нашей повседневной жизни так обычно и происходит — мы используем те или иные параметры опосредованно. Если взять уже упоминавшийся нами пример с компьютером, то у него есть такой параметр, как размер свободной памяти на жестком диске. Однако вряд ли владелец компьютера для определения этого параметра будет непосредственно отсчитывать байты — для этой цели служат специальные подпрограммы.

Такое опосредованное обращение к данным позволяет избежать во многих случаях непредвиденных нежелательных изменений параметров. В Turbo Pascal с этой целью используется специальное зарезервированное слово private (приватный), в принципе запрещающее непосредственное обращение к тем или иным данным и методам объекта вне модуля, в котором описан объект. В версии 7.0 приватная секция может размещаться в любом месте объекта (раньше — только в конце после обычных, доступных параметров и методов). Так, если необходимо запретить из основной программы обращаться к данным объекта типа tConniection (напомним, что основная программа редактора находится в отдельном файле), этот тип можно описать следующим образом:

tуре
tConnection = object
procedure PutPredElem(PredEl: Pointer);
procedure PutNextElem(NextEl: Pointer);
function GetPredElem: Pointer;
function GetNextElem: Pointer private
PredElem: Pointer;
NextElem: Pointer;
end;

Если приватная секция находится не в конце объекта, то для ограничения диапазона действия зарезервированного слова private следует после приватной секции поместить зарезервированное слово public (доступный извне) — только в версии 7.0:

type
tConnection = object
private
PredElem: Pointer;
NextElem: Pointer;
public
procedure PutPredElem(PredEl: Pointer);
procedure PutNextElem(NextEl: Pointer);
function GetPredElem: Pointer;
function GetNextElem: Pointer
end;

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

Если из рассматриваемого примера взять тип tStructure (структура), то структуру текста можно задать ее начальным и конечным элементами и связями между отдельными элементами структуры. Связи между отдельными элементами задаются типом tConnection, и было бы нецелесообразно при создании нового типа tStructure заново задавать эти связи. Чтобы этого избежать, в ООП заложено свойство наследования характеристик одного объекта другим. Для этого один из объектов объявляется потомком другого, который, в свою очередь, становится предком этого нового объекта. Потомок наследует все параметры своего предка и его методы, поэтому вторично их описывать нет необходимости, а использовать можно. Это существенно упрощает запись схожих объектов, если установить между ними наследственную связь.

В примере редактора используемые объекты образуют наследственную структуру, которая изображена на рис. 2. В частности, когда задается тип tStructure, его можно объявить потомком типа tConnection (для этого следует после зарезервированного слова object в круглых скобках указать имя типа-предка):

type
tStructure = object(tCornection)

FirstElem: Pointer;
LastElem: Pointer;
constructor Init;
procedure PutFirstElem(FirstEl: Pointer);
procedure PutLastElem(FirstEl: Pointer);
function GetFirstElem: Pointer;
function GetLastElem: Pointer;
procedure InitElem(var NewPoint: Pointer); virtual;
procedure DispElem(PointDel: Pointer); virtual;
procedure PutConnection(FirstPoint, SecondPoint: Pointer);
procedure NewEl(PointPredEl ,PointNextEl: Pointer);
procedure DelEKPointDel: Pointer);
end;

В этом типе имеются собственные данные: FirstElem (указатель на первый Элемент структуры), LastElem (указатель на последний элемент структуры) и методы: Init (инициализация структуры), PutFirstElem (задание значения указателя на первый элемент), PutLastElem (задание значения указателя на последний элемент, GetFirstElem (получение значения указателя на первый элемент), (JetLastElem (получение значения указателя на последний элемент), InitElem (инициализация — создание нового элемента), DispElem (удаление элемента), PutConnection (задание связей между двумя элементами), NewEl (помещение в структуру нового элемента), DelEl (удаление из структуры элемента). Кроме них этот тип наследует от предка tConnection его данные PredElem и NextElem и методы PutPredElem, PutNextElem, GetPredElem и GetNextElem.

В Turbo Pascal непосредственный предок может быть только один. Однако он, в свою очередь, может быть потомком другого типа и т. д. В этом случае потомок наследует характеристики всех своих предков. Так, например, тип tLine (строка) является потомком типа tStructure, который, в свою очередь, является потомком типа tConnection. В связи с этим объект типа tLine может использовать параметры и методы в том числе и типа tConnection.

14.2.3. Полиморфизм

В рассматриваемом примере может возникнут необходимость помещения того или иного символа в элемент строки, строку или весь текст. Для этого можно включить соответствующие методы в типы tElLine (элемент строки), tLine (строка) и tText (текст). Естественно, что эти действия будут отличаться в зависимости от того, куда помещается символ. Если символ помещается в элемент строки, то необходимо знать только номер позиции, куда следует поместить символ. Если символ помещается в строку, то сначала, исходя их координаты X в строке, следует определить, в какой конкретно элемент строки (получить указатель на этот элемент) и в какую позицию в этом элементе необходимо поместить символ. Затем уже можно размещать символ в соответствующем элементе. Если же символ следует поместить в текст, то сначала по координате Y следует определить строку (получить указатель на эту строку), а затем уже выполнить все действия, связанные с размещением символа в строке. Таким образом, следует иметь три разные подпрограммы для трех различных типов. Т. к. все они выполняют в конце концов одно и то же действие — размещают символ в соответствующем месте, было бы заманчиво все их назвать одним именем. В языке Паскаль это делать запрещено — все подпрограммы должны иметь уникальные имена. Для ООП в Turbo Pascal сделано исключение — все эти подпрограммы могут иметь одно и то же имя.

В этой возможности — иметь несколько подпрограмм с одним и тем же именем — и заключается полиморфизм ООП. Вопрос, какая же конкретно подпрограмма будет использоваться в том или ином случае, определяется типом конкретного объекта, использующего эту подпрограмму. Так, если объект типа tText, то будет использована подпрограмма, размещающая символ в тексте, если типа tLine, то подпрограмма, размещающая символ в строке, и т. д.

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

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

Определение: Классы в Object Pascal это специальные типы, которые представляют собой единство трех сущностей: полей, методов, и свойств.

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

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

Пример описания класса объекта:

TMyClass=class(TObject)

Field: Integer;

end;

MyClass: TMyClass;

begin

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

MyClass.Field:=0;

end;

Важнейшие принципы объектно-ориентированного программирования.

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

  1. инкапсуляция
  2. наследование
  3. полиморфизм

Как выше было сказано класс это единство 3х сущностей: полей, методов и свойств.

Определение: Объединение 3х сущностей (полей, методов, свойств) в абстрактных типах данных, называемых классы объектов, в единое целое называется инкапсуляцией.

Инкапсуляция означает объединение данных и действий над ними в одном объектном типе.

Инкапсуляция позволяет изолировать класс от остальных частей программы, сделать его самодостаточным для решения конкретной задачи. В результате класс всегда несет в себе некоторую функциональность. Например, класс TForm содержит (или инкапсулирует) в себе все необходимое для создания Windows – окна, класс TMemo представляет собой полнофункциональный текстовый редактор.

Инкапсуляция представляет собой мощное средство обмена готовыми к работе программными заготовками.

Т.о. библиотека классов Delphi это фактически набор кирпичиков созданных программистами – разработчиками для построения программ пользователей.

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

TChildClass= class(TParentclass)

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

TClass=class (TObjeсt)

TClass=сlass

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

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

TInterfaceObject Exception

TPersistent

TWinControl

Полиморфизм – это свойство классов решать схожие по смыслу проблемы разными способами.

В рамках Object Pascal поведенческие свойства класса определяются набором входящих в него методов.

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

21.Понятие объекта и экземпляра объекта в Object Pascal.

Объекты можно представлять несколькими способами.OP использует ссылочную модель объектов. Каждая переменная типа класс называется объектом, но реально содержит лишь указатель на область памяти, в которой содержится значение реального объекта. Эта область оперативной памяти называется экземпляром объект. Несмотря на кажущуюся сложность, такая модель является очень мощной и удобной. Единственная проблема — программист должен вручную отводить память под так называемый экземпляр объекта.Три основных языковых понятия в ОР, реализующих принципы ООП:1) класс — пользовательский тип данных, описывающий поля, свойства, методы и события, которые будут у всех объектов этого класса (переменных этого типа).2) объект — это переменная типа класс, фактически является типизированным указателем.3)экземпляр объекта — место в оперативной памяти, где собственно хранятся значения полей, свойств и адреса методов объекта (по сути, является динамической переменной).

Однако есть определенное отличие динамической переменной от экземпляра объекта. Если для получения значения динамической переменной используется операция разыменования (^), то при работе с экземплярами объекта эту операцию проводить не нужно.Текст фрагмента программы, где используется объект объявленного класса, может выглядеть следующим образом.Создание и уничтожение объекта класса:

// Записываем значение в его свойство

В приведенном примере в разделе var описана переменная AButton — объект класса TMyButton. В тексте программы происходит создание объекта, модификация его свойства и в конце — его удаление. Для того чтобы понять, как создается и удаляется объект, необходимо более детально рассмотреть реализацию этого понятия в языке ОР.

Чтобы создать экземпляр объекта, необходимо вызвать метод Create. который является конструктором объекта, а результат его выполнения присвоить переменной-объекту. При создании нового экземпляра объекта класса конструктор применяется к классу, а не к объекту, например, TMyButtonCreate. Такой конструктор есть у всех классов, поскольку он описал как конструктор класса TObject, от которого наследуются все остальные классы. Если создается экземпляр объекта (отводится место в оперативной памяти), то впоследствии его необходимо уничтожить (освободить память). Эта операция выполняется с использованием деструктора. В классе TObject определен деструктор Destroy, который, таким образом, есть у всех классов. Тем не менее, для уничтожения экземпляра объекта рекомендуется использовать метод Free, который также унаследован от класса TObject. Этот метод надежней, поскольку он сначала проверяет не равен ли указатель на экземпляр объекта nil и только затем вызывает деструктор Destroy.

22.Конструкторы и деструкторы. Ключевое слово self.

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

В Delphi ключевое слово self часто используется, когда нужно явно обратиться к текущей форме в одном из ее методов. Типичный пример динамическое создание компонента, когда в конструктор компонента Create необходимо передать параметр Owner (указать владельца), а затем присвоить то же значение свойству Parent (указать родителя). Тем и другим для создаваемого компонента является текущая форма, и лучшим способом указать на нее является использование self.

Конструкторы — это специальные методы, создающие и инициализирующие объекты. Объявление конструктора выглядит как объявление метода, но предваряется ключевым словом constructor. В качестве имени конструктора обычно используют Create.

Если конструктор применяется к классу, то сначала происходит создание объекта — под него отводится память, значения всех полей , при этом порядковым типам в качестве начального значения задается значение с номером 0, указателям присваивается nil, строки задаются пустыми, а поля типа Variant получают значение Unassigned. Затем выполняется код конструктора, в котором обычно описывается инициализация элементов объектов класса (присваивание полям и свойствам начальных значений и пр.) и возвращается указатель на созданный экземпляр объекта.

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

Деструкторы — это специальные методы, уничтожающие объект освобождающие место в оперативной памяти. Объявление деструктора выглядит также как объявление процедуры, но предваряется ключевым слово destructor. В качестве имени деструктора обычно задают Destroy. Реализация деструктора, как правило, завершается вызовом наследуемого деструктора с помощью ключевого слова inherited, чтобы освободить память от наследуемых полей.

Вместо того, чтобы вызывать деструктор Destroy напрямую, принято вызывать специальную процедуру Free, действие которой состоит в том, что он вызывает деструктор Destroy только в том случае, если объект существует (то есть ссылка на него не равна nil

При рассмотрении конструкторов и деструкторов необходимо учитывать, что их нельзя использовать для компонентов Delphi (для объектов любых классов, произошедших от класса TComponent). Любой компонент, попавший при визуальном проектировании из Палитры компонентов в приложение, определяет своим владельцем форму (контейнер), на которую он помещен и для него — конструкторы и деструкторы вызываются автоматически, при создании (уничтожении) владельца, то есть формы. Создание и уничтожение форм делает приложение (глобальный объект с именем Application), для этого в файле проекта вызывается метод Application.CreateForm.

Понимание ООП на примере Паскаля

В продолжении предыдущей темы, что база программирования важнее самого языка, покажу на примере Turbo Pascal 7.0, что такое объекты в объектно-ориентированном программировании. (Чтобы было понятно, ООП в Паскале появился в 1989 году в версии TP 5.5.)

Если начинать изучать объекты, например с PHP, то не будет понимания, что это вообще такое. Зачем, например оператор new, что такое поля или методы? Такие вещи воспринимаются как часть языка, а всё что внутри — чёрный ящик, где происходят какие-то скрытые процессы. Вообще ООП — это парадигма, то есть описывает вроде как только общие принципы, а конкретная реализация зависит от языка. Но, на самом деле ООП в итоге всё равно превращается в обычный исполняемый код. На мой взгляд важно понимать откуда он берётся.

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

В ООП объект — это тип данных. Есть простые типы данных. Например тип Byte используется для хранения информации размером в один байт. Тип Word — занимает 2 байта и служит для хранения чисел от 0 до 65535.

Когда объявляется переменная, программа выделяет область памяти и именует её для идентификации. При этом компилятор обязательно проверит тип данных, которые пытаются в эту область записать. Если это тип Char (символьный), то в него никак не получится записать число.

В Паскале переменные объявляются в секции var (от «variables»):

var a: integer; s: string;

В данном случае типы integer и string — простые, о них знает компилятор Паскаля. Но, можно сделать и свои типы данных. Например объявим тип myInteger, который будет целочисленным LongInt.

type myInteger: LongInt; var a: myInteger;

Секция type используется для декларирования составных типов. То есть вначале нужно описать тип данных, потом описать переменную этого типа, а уже после можно её использовать в самой программе. Из всех типов данных есть один особенный — процедурный — он указывает на функцию или процедуру.

Процедурный тип данных

Вначале я приведу полный код программы.

program sample_type_function;  < TYPES >type TMyFunc = function: string; < FUNCTIONS >function f1: string; begin f1 := 'this f1'; end; function f2: string; begin f2 := 'this f2'; end; function f3(s: string): string; begin f3 := 'this f3'; end; < VARIABLES >var myFun: TMyFunc; < START PROGRAM >begin writeln('=== MyFun ==='); myFun := f1; writeln(myFun); myFun := f2; writeln(myFun); < ! ERROR: TYPE MISMATCH > < myFun := f3; >writeln('. press Enter . '); readln; end.

Здесь объявляется процедурный тип TMyFunc.

В Паскале принято идентификаторы типов именовать с большой буквы «T».

Ключ указывает компилятору использовать отложенное определение функций и процедур. За это отвечает опция компилятора «Force far calls».

Сама функция при этом вообще никак не определена. То есть глядя на это объявление, единственное, что мы можем увидеть — только то, что функция не имеет параметров и возвращает тип string.

Мы объявляем несколько функций: f1, f2, которые точно также не имеют параметров и возвращают строку. То есть по сути они полностью соответствуют по параметрам типу TMyFunc.

А вот функция f3, уже имеет входящий параметр, что делает её несовместимой с TMyFunc.

Имя функции, её параметры и тип возвращаемого результата определяют т.н. сигнатуру функции. В последующих версиях Паскаля (и других ЯП), сигнатура используется для перегрузки (перекрытия), то есть когда можно определить функцию с одним именем, но разными параметрами. Компилятор проверит входящие параметры и вызовет нужную функцию. Перекрытие функций есть не во всех языках, например его нет в PHP.

Теперь, чтобы использовать тип TMyFunc, нужно объявить переменную — у нас это myFun. А дальше она используется как обычно, путем присваивания. В первом примере вывода она будет f1, а во втором f2.

А вот присвоить f3 не получится. Компилятор сразу ругнётся с ошибкой «TYPE MISMATCH» (несовместимость типов).

Функции (и процедуры) — это подпрограммы. Они располагаются отдельными блоками в памяти и имеют точку входа. То есть вызывая f1, программа обратится к ячейке памяти, где начнётся выполняться эта подпрограмма. После выполнения произойдёт возврат из подпрограммы в основную программу.

Когда мы присваиваем myFun := f1; на самом деле создаётся лишь ссылка на адрес подпрограммы. Компилятор понимает, что myFun — процедурный тип, а значит в этой переменной будет храниться только адрес подпрограммы. Если мы попытаемся вызвать myFun без предварительного присваивания, то программа вывалится с ошибкой, поскольку переменная изначально ничего не содержит и ни на что не ссылается.

Тип Record (запись)

Другой тип данных — запись, которая хранит поля. Опять же приведу полный код программы.

program sample_rec;  < TYPES >type TMyFunc = function: string; TMyRec = record a: integer; c: char; f: TMyFunc; end; < FUNCTIONS >function f1: string; begin f1 := 'this f1'; end; < VARIABLES >var myRec: TMyRec; < START PROGRAM >begin writeln('=== MyRec ==='); myRec.a := 1; myRec.c := 'a'; myRec.f := f1; writeln(myRec.a); writeln(myRec.c); writeln(myRec.f); writeln('. press Enter . '); readln; end.

Здесь объявляется тип TMyRec, который состоит из полей a, с и f. Работать с таким типом данных нужно аналогично: объявляется переменная (myRec), а после используется в коде. Чтобы получить доступ к полям используется символ точки «.».

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

Нетрудно заметить, что процедурный тип и запись уже сильно напоминают объекты. Вот если мы их объединим, то и получим тип Объект.

Тип Object

program sample_obj;  < TYPES >type TMyObj = object a: integer; c: char; procedure p; end; < FUNCTIONS >procedure TMyObj.p; begin a := 1; c := 'a'; end; < VARIABLES >var MyObj: ^TMyObj; < START PROGRAM >begin writeln('=== MyObj ==='); new(MyObj); MyObj^.p; writeln(MyObj^.a); writeln(MyObj^.c); MyObj^.a := 2; MyObj^.c := 'b'; writeln(MyObj^.a); writeln(MyObj^.c); writeln('. press Enter . '); readln; end.

Для объявления объектов используется ключевое слово object. Внутри описываются поля так же, как и в записи. А процедуры/функции объявляются в полном виде (параметры и тип результата) и уже называются методы. Для доступа к полям и методам используется аналогичная нотация в виде точки.

В методах, в отличие от обычных процедур, автоматически доступны поля объекта. В других языках для этого приходится использовать специальное ключевое слово «this», указывающее на текущий объект. Именно поэтому в TMyObj.p нет секции var: мы сразу обращаемся к полям объекта.

Для работы с объектом нужно также создать переменную. Но здесь уже следует использовать «шляпки» — символ «^»: в Паскале это означает указатель, который ссылается на тип данных. Если по простому, то это ссылка на область памяти. В данном случае переменная MyObj как бы ссылается на TMyObj. Это довольно типовое поведение для ЯП. Например в PHP когда вы присваиваете какой-то переменной существующий объект, то обе переменные будут ссылаться на один и тот же объект в памяти. В Паскале переменная также указывает именно на объект в памяти, только делается это явно.

Объект требует инициализации. Для этого используется языковая конструкция new() , которая выделяет память под объект, и если необходимо, инициализирует поля, выполняет метод конструктора и т.п.

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

Соберём воедино

Итак, объект — это некая область памяти, содержащая поля данных и подпрограммы. Перед работой с объектом, необходимо определить его тип данных. В Паскале это осуществляется в секции type, как и для любого типа данных. В более поздних версиях Паскаля (и в других языках) для декларирования типа объекта используется ключевое слово class. Так например в PHP:

// TYPES class TMyObj < var $a; var $c; function p() < $this->a = 2; $this->c = 'b'; > > // START PROGRAM $myObj = new TMyObj(); $myObj->p(); echo $myObj->a; echo $myObj->c;

Фактически это тот же самый код из Паскаля, только в PHP часть работы оказалась скрыта: это делает код проще и удобней. Но при этом из кода PHP мы никогда не узнаем, что на самом деле есть указатели и переменные, которые также ссылаются на выделенную память объекта. Причём это отличается от поведения обычных php-переменных. Но узнать об этом можно только в справке. 🙂

В этом плане Паскаль даёт хорошее представление даже о таких сложных вещах, как ООП. После его изучения, тот же PHP выглядит достаточно просто: обычные подпрограммы и классы, которые в большинстве случаев нужны только для ограничения области видимости. На мой взгляд, после Паскаля, большинство вещей в программировании становятся простыми и понятными, что позволяет перейти к использованию любого другого современного языка.

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

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