Различают файлы трех видов: текстовые файлы, типизированные файлы и нетипизированные файлы.
Текстовые файлы состоят из последовательностей символов, разбитых на строки. В Object Pascal предопределен тип TextFile, соответствующий текстовому файлу. Таким образом, объявление файловой переменной может иметь вид:
var <имя файловой переменной>: TextFile;
Работа с текстовыми файлами осуществляется процедурами и функциями файлового ввода/вывода. Основные процедуры чтения – Read, ReadLn, Write и WriteLn.
Типизированные файлы являются двоичными файлами, содержащими последовательность однотипных данных. Объявление файловых переменных таких файлов имеет вид:
var <имя файловой переменной>: file of <тип данных>;
var F: file of real; // пример
Процедуры чтения и записи Read и Write для типизированных файлов не отличаются от соответствующих процедур для текстовых файлов. Не определены процедуры Readln и Writeln, но есть процедура Seek, позволяющая перемещаться по файлу не только последовательно, как в текстовых файлах, а сразу переходить к требуемому элементу. Имеется также функция FilePos, возвращающая текущую позицию в файле.
Нетипизированные файлы – это двоичные файлы, которые могут содержать самые различные данные в виде последовательности байтов. Программист при чтении этих данных сам должен разбираться, какие байты к чему относятся. Тип файловой переменной нетипизированного файла объявляется следующим образом:
var <имя файловой переменной>: file;
Открытие нетипизированных файлов осуществляется процедурами Reset и Rewrite, синтаксис которых несколько отличен от аналогичных процедур для других видов файлов тем, что в этих процедурах указывается размер одной записи в байтах. Для записи и чтения данных в нетипизированных файлах имеются процедуры BlockRead и BlockWrite, которые читают или записывают указанное в них число записей.
Указатель является величиной, указывающей на некоторый адрес в памяти, где хранятся какие-то данные. Указатели бывают типизированные, указывающие на данные определенного типа, и нетипизированные (типа pointer), которые могут указывать на данные произвольного типа. Используются указатели обычно для работы с объектами в динамически распределяемой области памяти, особенно при работе с записями.
Объявление указателя имеет вид:
type <имя типа указателя> = ^<тип данных>;
F1 = ^real; // типизированный указатель
F2 = pointer; // нетипизированный указатель
Предопределена операция @, возвращающая адрес своего операнда: переменной, функции, процедуры, метода. Иначе говоря, операция @ создает указатель на свой операнд. При этом если X – переменная, то @X возвращает адрес X, причем при директиве компилятора {$T–} (включена по умолчанию) тип результата pointer. В случае функции или процедуры с именем F – @F возвращает точку входа, причем тип результата всегда pointer. Также существует предопределенная константа nil, которая обычно присваивается указателям, которые в данный момент ни на что не указывают.
Чтобы получить доступ к данным, на которые указывает типизированный указатель, надо применить операцию его разыменования. Она записывается с помощью символа ^, помещаемого после указателя. Например, для того, чтобы по адресу объявленного типизированного указателя на тип real F1 находилось какое-либо число, необходим оператор вида F1^:=0.18;. Операция разыменовывания не применима к типу pointer. Для разыменовывания подобного указателя сначала необходимо привести его к другому типу.
Процедурные типы данных также являются указателями на любые определенные в программе процедуры и функции. При их использовании необходимо следить за соответствием параметров, передаваемых переменной процедурного типа, формальному набору параметров функции или процедуры, на тип которой указывает процедурная переменная.
В переменных типа variant могут храниться данные любых типов, кроме структур и указателей. Тип variant имеет смысл использовать в тех случаях, когда тип того или иного объекта заранее не известен или когда какие-то функции и процедуры требуют именно такой тип аргументов. При этом надо иметь в виду, что затраты памяти и времени на работу с переменными типа variant больше, чем при работе с обычными типами. К тому же недопустимые операции с переменными этого типа приводят к ошибкам времени выполнения, тогда как аналогичные недопустимые операции с переменными других типов выявляются на этапе компиляции.
Переменные типа variant занимают 16 битов и содержат код типа и значение переменной или указатель на это значение. Узнать действительный тип значения переменной variant можно с помощью функции VarType, возвращающей значение типа TVarData, содержащее код типа. Переменные типа variant могут использоваться в выражениях как операнды любых операций, кроме ^, is и in, совместно с другими величинами типов variant, integer, real, string, boolean. Компилятор в этих случаях автоматически осуществляет приведение типов величин в выражении.
Класс – это тип данных, определяемый пользователем. То, что в Delphi имеется множество предопределенных классов, не противоречит этому определению – ведь разработчики Delphi тоже пользователи Object Pascal. Понятие объекта (object) использовалось при зарождении объектно-ориентированного программирования при переходе от Pascal к Object Pascal. В настоящее время тип object практически эквивалентен классу. В научно-технической литературе можно встретить иную интерпретацию термина "объект" – как конкретный экземпляр или инициализированную переменную класса.
Класс должен быть объявлен до того, как будет объявлена хотя бы одна переменная этого класса. Синтаксис объявления следующий.
Type
<имя класса> = Class (<имя класса - родителя>)
public {доступно всем}
<поля, методы, свойства, события>
published {видны в Инспекторе Объекта и изменяемы}
<поля, свойства>
protected {доступ только потомкам}
<поля, методы, свойства, события>
private {доступ только в модуле}
<поля, методы, свойства, события>
end;
Имя класса может быть любым допустимым идентификатором, однако идентификаторы большинства классов принято начинать с символа "T". Имя класса-родителя может не указываться. Тогда предполагается, что данный класс является непосредственным наследником TObject – наиболее общего из предопределенных классов. Таким образом, эквивалентны следующие объявления:
type TMyClass = class
...
end;
type TMyClass = class(TObject)
...
end;
Класс наследует поля, методы, свойства, события от своих предков и может отменять какие-то из этих элементов класса или вводить новые. Доступ к объявляемым элементам класса определяется тем, в каком разделе они объявлены.
Раздел public (открытый) предназначен для объявлений, которые доступны для внешнего использования. Это открытый интерфейс класса. Раздел published (публикуемый) содержит открытые свойства, которые появляются в процессе проектирования на странице свойств Инспектора Объектов и которые, следовательно, пользователь может устанавливать в процессе проектирования. Раздел private (закрытый) содержит объявления полей, процедур и функций, используемых только внутри данного класса. Раздел protected (защищенный) содержит объявления, доступные только для потомков объявляемого класса. Как и в случае закрытых элементов, можно скрыть детали реализации защищенных элементов от конечного пользователя. Однако, в отличие от закрытых, защищенные элементы остаются доступны для программистов, которые захотят производить от этого класса производные объекты, причем не требуется, чтобы производные объекты объявлялись в этом же модуле.
Объявления полей выглядят так же, как объявления переменных или объявления полей в записях. Объявления методов в простейшем случае также не отличаются от обычных объявлений процедур и функций.
Интерфейсы во многом напоминают интерфейсную часть классов. Но они содержат только объявления методов и не содержат их реализацию, которая должна осуществляться в классах, поддерживающих данный интерфейс. Однако в приложении могут создаваться переменные типа интерфейса. Через них можно обращаться к объявленным в интерфейсе методам объектов классов, использующих данный интерфейс, точно так же, как это делается при непосредственном обращении к объектам. Особенность заключается в том, что при обращении через интерфейс можно работать с объектами разных классов (в том числе с объектами классов, не имеющими общего предка), в которых определены используемые методы. Таким образом, возможна реализация полиморфного поведения объектов совершенно различных классов. Необходимо отметить, что интерфейсы обеспечивают возможность множественного наследования, отсутствующую в классах Object Pascal.
Объявление интерфейса имеет вид:
type
<имя интерфейса>=interface(<имя интерфейса-родителя>)
['GUID']
<список элементов>
end;
Имена интерфейсов принято начинать с символа "I". Имя интерфейса-родителя может отсутствовать. В этом случае интерфейс наследует непосредственно классу IUnknown – базовому классу всех интерфейсов. Интерфейс, подобно классу, наследует от своих родителей все методы, но не наследует их реализацию. Может также отсутствовать указание GUID – глобального уникального идентификатора, который задается строкой шестнадцатеричных символов (от 0 до F) вида ['{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}'].
Объявление интерфейса в основном аналогично объявлению класса и имеет следующие ограничения:
список элементов может включать только методы и свойства (с соответствующими методами для чтения и записи), но не может включать поля;
все методы интерфейса открытые (public), т.е. при объявлении метода необходима инструкция stdcall;
поскольку реализация методов интерфейса определяется в классе, поддерживающем данный интерфейс, то интерфейсные методы не могут быть виртуальными, динамическими, абстрактными, не могут перегружаться, также интерфейс не имеет конструктора и деструктора.
Оператор присваивания записывается в виде: <переменная> := <выражение>; где <переменная> – это переменная или типизированная константа любого типа, включая разыменованный указатель, объект, структуру и т.д., а <выражение> – любое допустимое выражение, совместимое по типу с переменной в левой части оператора. Оператор вычисляет значение выражения, записанного как правый операнд операции присваивания :=, и присваивает полученное значение переменной в левой части оператора. Например, оператор I:=3 присваивает переменной I значение 3, а оператор I:=I+1 увеличивает значение переменной I на 1.
Применительно к объектам надо четко представлять различие между оператором присваивания и методом копирования параметров Assign, свойственным многим классам.
Оператор goto позволяет прервать обычный поток управления и передать управление в произвольную точку кода, помеченную меткой. В тексте программы метка отмечает точку, в которую передается управление, и может располагаться в любом месте блока, как после оператора goto, так и до него. Точка, в которую может передаваться управление, помечается именем метки, после которого следует двоеточие. Например:
L1: <оператор, на который передается управление>
Сам оператор goto имеет форму:
goto <метка>;
Все три элемента конструкции: объявление меток, сами метки и операторы goto, передающие на них управление, должны размещаться в пределах одного блока.
Оператор условного перехода if предназначен для выполнения тех или иных действий в зависимости от истинности или ложности некоторого условия. Условие задается выражением, имеющим результат типа boolean.
В общем виде оператор условного перехода имеет вид:
if <условие> then <оператор1> else <оператор2>;
Если условие возвращает true, то выполняется первый из указанных операторов, в противном случае выполняется второй оператор. Заметим, что в конце первого оператора перед ключевым словом else точка с запятой не ставится. Ветвь с else может отсутствовать. Тогда при невыполнении условия управление передается следующему за конструкцией if оператору. Например, в результате выполнения операторов
C := A;
if B > A then C := B;
переменная C станет равна максимальному из чисел A и B, поскольку оператор C:=B будет выполнен только при B>A.
При вложенных конструкциях if могут возникнуть неоднозначности в понимании того, к какой из вложенных конструкций if относится элемент else. Компилятор всегда считает, что else относится к последней из конструкций if, в которой не было раздела else. Например, в конструкции
if <условие1> then if <условие2> then <оператор1>
else <оператор2>;
else будет отнесено компилятором ко второй конструкции if, т.е. <оператор2> будет выполняться в случае, если первое условие истинно, а второе ложно. Иначе говоря, вся конструкция будет прочитана как
if <условие1> then begin
if <условие2> then <оператор1> else <оператор2>
end;
Если же необходимо отнести else к первому if, то это надо реализовывать как составной оператор, т.е. записать в явном виде с помощью операторных скобок begin...end.
if <условие1> then begin
if <условие2> then <оператор1>
end
else <оператор2>