As — Ключевое слово Delphi
Прежде чем перейдем к дальнейшему описанию языка Delphi, формально опреде-
лим несколько терминов. Во-первых, это слово «идентификатор». Идентификатор —
это строка символов, используемая для именования некоторого элемента программы.
Это может быть переменная, запись, функция, процедура или конструкция более вы-
сокого уровня, например сама программа.
Идентификатор может иметь любую длину, однако в языке Delphi только первые
его 255 символов являются значимыми (что более чем достаточно!). Идентификатор
должен начинаться с буквы или символа подчеркивания (_) и не может содержать
пробелов. После первого символа идентификатора можно использовать буквы, цифры
и символы подчеркивания. Как и в зарезервированных словах, в идентификаторах
можно использовать как строчные, так и прописные буквы (компилятор их не разли-
чает). Приведенные ниже идентификаторы означают одно и то же.
CalculateValue
calculateValue
calculatevalue
CALCULATEVALUE
Ключевые слова не могут быть идентификаторами.
Далее рассмотрим лексемы. Это минимальные значимые единицы текста в про-
грамме. Они представлены такими категориями, как специальные символы, иденти-
фикаторы, метки, числа и строковые константы.
Программа, написанная на языке Delphi, состоит из лексем и разделителей, при-
чем разделитель представляет собой пробел или комментарий. Две соседних лексемы,
если они представляют собой зарезервированное слово, идентификатор, метку или
число, должны быть отделены друг от друга одним или несколькими разделителями.
В Delphi используются следующие подмножества набора символов кода ASCII.
• Буквы английского алфавита от А до Z и от а до z.
• Цифры — арабские цифры от 0 до 9.
• Шестнадцатеричные цифры — арабские цифры от 0 до 9, буквы от А до F и бу-
квы от а до f.
• Пробелы — символ пробела ($32) и все управляющие символы кода ASCII ($0-$31),
включая символ конца строки или символ возврата каретки ($13). Это шестна-
дцатеричные числа, так как перед ними стоит символ доллара «$».
Теперь определим смысл слова «выражение». Это фрагмент языка программирова-
ния, представляющий способ вычисления некоторого значения.
И наконец, определим смысл слов «операнд» и «оператор».
Операнд — часть выражения, над которым производятся операции. Например,
в выражении, присваивающем А сумму в и с (А := В+С;), А, в, с являются операн-
дами, а над значениями, представленными идентификаторами А й в , производится
операция суммирования. Идентификатор — это строка символов, используемая для именования некоторого
элемента программы.
Лексемы — это минимальные значимые единицы текста в программе.
Выражение — это фрагмент языка программирования, представляющий способ вы-
числения некоторого значения.
Операнд — часть выражения, над которым производятся операции.
Оператор — действие, которое может быть выполнено над одним или несколькими
операндами.
Оператор — действие, которое может быть выполнено над одним или несколькими
операндами. Если обратиться к вышеприведенному примеру, то оператором является
знак плюс (+). Хотя в некоторых случаях оператором можно назвать целое выражение,
заканчивающееся точкой с запятой. Более правильно такие операторы надо называть
структурированными операторами. Например, выражение
while i:=0 to 10 do x := i ;
можно назвать оператором, так как здесь выполняется операция цикла над пере-
менной X.
Теперь можно переходить непосредственно к ключевым словам. Обычно ключевые
слова пишутся строчными буквами, но Delphi безразличен к регистру клавиатуры, по-
этому можно использовать в своей программе как строчные (нижний регистр), так
и прописные (верхний регистр) буквы. Я рекомендую использовать какой-то один
стиль написания, например, тот, к которому вы уже привыкли. Но если вы только на-
чинаете программировать, то лучше использовать общепринятые правила и писать
ключевые слова строчными буквами. В табл. 3.1 приведен перечень всех ключевых
слов с кратким комментарием.
Таблица 3.1. Ключевые слова
Ключевое слово Комментарий
and Булев оператор И
array Массив
as Используется при проверке соответствия типов, определяет объект как операнд
asm Используется для выделения ассемблерного кода
begin Начало блока
case Оператор выбора. Используется при выборе из многих вариантов
class Определяет тип «класс»
const Определяет константы, т.е. неизменяемые переменные. Однако в Delphi есть
режим, допускающий изменение констант в теле программы
constructor Специальный метод класса, необходимый для создания и инициализации
экземпляра класса (объекта)
destructor Специальный метод класса, необходимый для разрушения объекта
d i s p i n t e r f a c e Определяет тип интерфейса
div Целочисленное деление
do Определяет начало исполнимой части в операторах цикла, конструкции
t r y . . . except и в операторе w i t h
downto Определяет направление итерации в операторе f o r
else Используется в операторах выбора case, условном операторе i f и в операторе
проверки исключений t r y . . .except
end
except
e x p o r t s
f i l e
f i n a l i z a t i o n
f i n a l l y
for
f u n c t i o n
goto
i f
implementation
i n
i n h e r i t e d
i n i t i a l i z a t i o n
i n l i n e
i n t e r f a c e
i s
l a b e l
l i b r a r y
mod
n i l
not
o b j e c t
of
or
out
Обычно используется совместно с ключевым словом begin и отмечает конец
блока. Также ставится в конце описания типа, например класса или записи
Используется в операторе проверки исключений t r y . . . except
Определяет список экспортируемых процедур, функций и переменных
Устанавливает тип переменной как файл. Используется при работе с файлами
Определяет начало раздела, который в программе всегда выполняется
последним
Используется в операторе проверки исключений t r y . . . f i n a l l y
Используется в операторах цикла f o r . . . to и f o r . . .downto
Используется при объявлении функций
Переход на метку
Используется в операторах выбора i f . . . then и i f . . . then. . .else
Определяет раздел реализации, в котором находятся описания процедур,
функций, методов и коды разрабатываемой программы
После этого ключевого слова может указываться путь к необходимому модулю.
Также используется при работе с множествами
Дословно можно перевести как «унаследованный». Используется при работе
с классами, поддерживая возможности полиморфизма
Определяет раздел инициализации, который всегда располагается перед
разделом f i n a l i z a t i o n . Если раздела f i n a l i z a t i o n нет, то раздел
инициализации находится перед завершением программы. Выполняется сразу
после запуска программы, перед всеми другими операторами. Обычно
используется для инициализации переменных
Используется при работе с ассемблерным кодом. Устаревшее ключевое слово
к применению не рекомендуется
Определяет тип интерфейса. Используется при опережающем объявлении
интерфейса
Используется при проверке типов
Метка. Используется совместно с ключевым словом goto. Может быть
выражена любым идентификатором или числом от 0 до 9999
Директива-напоминание или рекомендательная директива. Используется
наравне с директивами p l a t f o rm и deprecated для напоминания об
особенностях стандартных типов, методов или модулей. Во время компиляции
вызывает появление предупреждающего сообщения
Остаток от деления целых чисел
Специальная константа, которая может быть присвоена любому указателю,
после чего считается, что указатель не ссылается ни на что
Булев оператор отрицания
Используется как альтернатива слову class. Сохранено в языке для
совместимости со старыми версиями. Не рекомендуется к использованию
Используется во многих операторах как связующее ключевое слово
Булев оператор ИЛИ
Используется при объявлении параметров процедуры, функции или метода.
Предупреждает о том, что данный параметр используется только для
выдачи значений
packed
p r o c e d u r e
program
p r o p e r t y
r a i s e
r e c o r d
r e p e a t
r e s o u r c e s t r i n g
s e a l e d
set
shl
s h r
s t r i n g
then
t h r e a d v a r
t o
t r y
type
u n i t
u n t i l
uses
var
while
w i t h
xor
Используется для более плотного размещения данных в структурированных
типах (массивы, множества, записи, файлы, классы)
Используется при объявлении процедур
Определяет имя программы, которое должно быть выражено
идентификатором
Используется при объявлении свойств
Используется при генерации исключений
Определяет тип записи
Используется в операторе цикла repeat. . . u n t i l
Определяет раздел объявления ресурсов
Используется при объявлении класса, запрещая наследование данного класса
Ключевое слово для объявления множества
Логический оператор сдвига влево
Логический оператор сдвига вправо
Используется при объявлении строковых типов
Используется в операторах i f . . . then и i f . . . t h e n . . .else
Используется при разработке многопоточных приложений
Используется в операторе f o r . . . t o
Используется в операторе проверки исключений t r y . . . f i n a l l y ,
t r y . . .except и в операторе выбора case
Определяет раздел объявления типов
Модуль. Обычно это функционально законченный фрагмент программы,
сохраняемый в файле с таким же именем
Используется в операторе repeat. . . u n t i l
Определяет раздел подключаемых модулей
Определяет раздел переменных
Используется в операторе w h i l e . . . do
Используется для определения идентификатора, который всегда записывается
с другими идентификаторами. Код получается более компактным и понятным
Булев оператор Исключающее ИЛИ
Есть еще несколько ключевых слов, о которых мы поговорим при изучении объектно-
ориентированного программирования и которые могут использоваться как директивы.
Именно эти ключевые слова выделяются жирным шрифтом в редакторе кода, хотя
кое-что может вызвать недоумение: почему, например, слово Break, которое в других
языках программирования является ключевым, в редакторе кодов не подсвечивается?
В Delphi это не ключевое слово, а процедура, и для нее существует отдельное описа-
ние в библиотеке. А процедура write не описана в библиотеках, так как ее код встро-
ен в компилятор. Сейчас мы не будем разбираться в этих тонкостях, а только отме-
тим, что все ключевые слова обычно пишутся строчными буквами, а процедуры
обычно начинаются с прописной буквы.
Написание идентификаторов также можно сделать нагляднее, если использовать слова,
отражающие назначение идентификатора, и начинать каждое слово с прописной буквы.
Например, идентификатор для счетчика символов можно написать так: SymbolCounter. Здесь слова использованы полностью, а разделение достигается за счет того, что второе
слово начинается с большой буквы. Каждый, кто будет читать программу, где использо-
ваны подобные идентификаторы, сможет понять ее без дополнительных комментариев.
Используйте это правило, и ваши программы станут понятнее и для вас самих. (К сожа-
лению, все идентификаторы должны писаться только английскими буквами, поэтому
учите английский, в настоящее время это язык общения программистов.)
As — Ключевое слово Delphi
Приведение типов в языках программирования позволяет представить какую-либо переменную или объект в виде другого типа. Существует достаточно большое количество способов, при помощи которых можно приводить объекты и переменные от одного типа к другому, но мы рассмотрим наиболее востребованные.
Мы уже не раз сталкивались с ситуациями, когда необходимо, например, получить строковое представление числа, или число из его строкового представления. Далеко за примерами таких ситуаций идти не придется, т.к. нам потребуется реализовать соответствующие преобразования даже при попытке отобразить число на экране, например в виде сообщения. Кроме того, ранее мы уже упоминали о существовании таких полезных функций, как StrToInt, IntToStr и т.п. Еще раз вспомним, как они работают:
[cc lang=»delphi»]var
a: integer;
b: string;
begin
a := 123;
b := IntToStr(a);
b := ‘234’;
a := StrToInt(b);
end;
[/cc]
Также существуют функции FloatToStr, StrToFloat, которые выполняют аналогичные действия, но только между строковыми и вещественными типами данных.
Т.е. большинство явных приведений в Delphi выполняется при помощи специальных функций. В число таких функций войдут и DateToStr, DateTimeToStr и многие другие.
Некоторые явные преобразования можно реализовывать и следующим образом:
[cc lang=»delphi»]var
c: char;
b: byte;
begin
b := 69;
c := Char(b); // В результате переменная «c» будет содержать букву «E»
end;[/cc]
В таком случае произойдет преобразование числа в символ по таблице ASCII. Тоже самое можно проделать и наоборот:
[cc lang=»delphi»]var
c: char;
b: byte;
begin
c := ‘E’;
b := Byte(c); // b = 69
end;[/cc]
Неявными считаются те преобразования типов переменных, которые происходят без нашего непосредственного контроля. Обычно они происходят посредством операций присвоения или сравнения. Например:
[cc lang=»delphi»]var
d: double;
a: integer;
begin
a := 100;
d := a;
if a = d then ShowMessage(‘a = d’);
end;[/cc]
Но важно учитывать те случаи, когда присваиваемое значение может принципиально не соответствовать переменной, в которую происходит присвоение. Примером таких случаев является попытка присвоить целочисленной переменной вещественное значение (или математическое выражение, которое сводится к вещественному значению), или попытка выйти за границы допустимых значений у какого-либо типа. Последняя ошибка в некоторых случаях может отлавливаться компилятором еще до компиляции («Constant expression violates subrange bounds»), но чаще всего такую ошибку допускают именно в run-time, т.е. во время работы программы, поэтому стоит с осторожностью применять неявные приведения.
Рассмотрим два класса, один из которых будет являться родительским для другого:
[cc lang=»delphi»]type
TParent = class
public
procedure ParentMethod;
end;
TChild = class(TParent)
public
procedure ChildMethod;
end;[/cc]
В случае с экземплярами классов, возможны привидения объекта дочернего класса к родительскому и наоборот. Рассмотрим эти приведения подробнее:
As — Ключевое слово Delphi
На текущий момент мы уже умеем создавать приложения Delphi. Тем самым все готово для того, чтобы приступать к изучению языка Object Pascal. Прежде всего, мы ознакомимся с синтаксисом языка — его алфавитом, структурой, ключевыми словами, а так же с правилами составления выражений и типами данных.
Алфавит и словарь языка
В программах Object Pascal могут использоваться любые символы из множества символов языка Object Pascal. К этому множеству относятся буквы латинского алфавита, арабские цифры, пробельные символы, разделители и специальные символы.
Прописные и строчные латинские буквы, а так же знак подчеркивания:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z _
0 1 2 3 4 5 6 7 8 9
Пробельные символы (разделители):
Пробел, табуляция, перевод строки, возврат каретки
Составные символы, образуемые сочетанием двух специальных:
ПРИМЕЧАНИЕ
Все прочие символы, включая символы кириллицы, также могут использоваться в Object Pascal, но только внутри строковых переменных и комментариев.
Последовательности, составленные из знаков алфавита, образуют слова. Слова отделяются друг от друга разделителями и несут в программе определенный смысл. В качестве разделителей могут использоваться как пробельные символы, так и некоторые специальные символы (например, ;), и комментарии. Сами слова можно разделить на такие группы, как ключевые слова; стандартные идентификаторы и пользовательские идентификаторы.
Что касается комментариев, то никакого смысла в программе они не несут, и могут использоваться для того, чтобы разработчик мог вставить пояснительный текст в код программы. Комментарии бывают двух видов — однострочные и многострочные. Для однострочных комментариев обычно используют составной символ «//», помещая его перед текстом самого комментария:
// эта строка полностью закомментирована x=5; // эта часть строки — комментарий
Для многострочных комментариев применяют символы < и >, либо (* и *):
Комментарии разных типов можно вкладывать друг в друга:
С помощью комментариев удобно исключать отдельные инструкции или целые блоки программы в процессе ее отладки.
Ключевые слова
Ключевые, слова являются неотъемлемой частью языка. Все они имеют однозначно определенный смысл, изменить который невозможно. Кроме собственно ключевых слов бывают еще и зарезервированные, т.е. те, которые могут стать ключевыми в последующих версиях языка. Полный список ключевых слов Delphi приведен в таблице 3.1.
Слово | Слово | Слово | Слово |
---|---|---|---|
absolute | export | nil | requires |
abstract | exports | nodefault | resident |
and | external | not | resourcestring |
array | far | object | safecall |
as | file | of | set |
asm | finalization | on | shl |
assembler | finally | or | shr |
at | for | out | stdcall |
automated | forward | overload | stored |
begin | function | override | string |
case | goto | package | then |
cdecl | if | packed | threadvar |
class | implementation | pascal | to |
const | implements | private | try |
constructor | in | procedure | type |
contains | index | program | unit |
default | inherited | property | until |
destructor | initialization | protected | uses |
dispid | inline | public | var |
dispinterface | interface | published | virtual |
div | is | raise | while |
do | label | read | with |
downto | library | readonly | write |
dynamic | message | record | writeonly |
else | mod | register | xor |
end | name | reintroduce | |
except | near | repeat |
Важно запомнить, что данные слова можно использовать только по их прямому назначению. Соответственно, вы не можете создавать какие-либо собственные идентификаторы (например, названия переменных), таким образом, чтобы если они совпадали с каким-либо ключевым словом.
Переменные и константы
Если ключевые слова имеют заранее предопределенный смысл, то значения констант и переменных определяет программист. Но прежде, чем приступить к исследованию констант и переменных, рассмотрим знак, используемый для некоторых операций, поскольку без этого невозможно дальнейшее продвижение. Речь идет о знаке равенства (=), который используется в Delphi в двух значениях — как оператор присваивания и как оператор сравнения.
В случае применения в качестве оператора присваивания, знак равенства используется совместно с символом двоеточия и имеет следующий общий синтаксис:
При присваивании используется 2 операнда, при этом подразумевается, что левому операнду (переменной) будет присвоено значение правого. Такая запись называется выражением.
ПРИМЕЧАНИЕ
Более точно выражение можно определить следующим образом: выражение — это набор данных, переменных, операторов и других выражений, которые приводятся к общему значению.
Другой вариант — это использование знака равенства для сравнения 2 операндов. В таком случае он используется самостоятельно:
Такая запись означает, что сравниваются левое (операнд1) и правое (операнд2) значения выражения. Однако специально для констант предусмотрен третий вариант, при котором константе присваивается значение правого операнда (как в случае обычного присваивания), но используется знак равенства без двоеточия:
Таким образом, мы переходим к самим константам и переменным, и начнем мы с констант, как наиболее типичного примера подстановки.
Суть использования констант состоит в том, что вместо какого-либо явного значения указывается его условное обозначение — константа. Допустим, что вы пишете программу, в которой неоднократно следует вычислять НДС. Разумеется, вы можете использовать в выражениях явное значение — 0.18 (18%). Но скорее всего, в программе найдется несколько мест, где в вычислениях требуется значение НДС. Таким образом, если НДС в очередной раз изменят, то вам придется отыскивать в программе все эти строки и вносить правку. В таких случаях на помощь приходят константы — достаточно один раз ее определить, а затем во всех тех местах, где требуется ее значение, указывать имя константы.
Определяются константы при помощи ключевого слова const (от англ. constant):
Теперь во всех выражениях, где требуется значение НДС, просто используется эта константа:
VATsumm := price * VAT;
В этом выражении задействована константа VAT и 2 переменных — VATsumm, которой присваивается значение, и price, которая используется для его вычисления. Впрочем, price в данном случае тоже может быть константой, в отличие от VATsumm. Дело в том, что константы определяются в момент написания программы, а при компиляции в код автоматически подставляются их действительные значения. Соответственно, для вычисляемых значений, равно как и для значений, которые может вводить пользователь, нужны не константы, а переменные. Так, если бы константа VAT была переменной, то можно было бы предусмотреть в программе опцию изменения значения НДС.
Переменные определяются при помощи ключевого слова var (от англ. variable):
Обратите внимание, что для переменных нужно указывать не только ее имя, но и тип данных. Собственно говоря, константы тоже получают тот или иной тип данных, только это происходит автоматически, в момент компиляции. Так что рассмотри типы данных, предусмотренных в Object Pascal.
ПРИМЕЧАНИЕ
Начиная с Delphi 4, в Object Pascal поддерживаются типизированные константы, значения которых можно изменять по ходу выполнения программы. Объявление констант такого типа производится следующим образом: «const : тип = значение». От обычных переменных они отличаются различиями в обработке компилятором, а так же тем, что для них всегда имеется какое-либо предопределенное значение.
Типы данных
Прежде, чем приступить к рассмотрению типов данных, постараемся понять, для чего это вообще надо. Прежде всего, Object Pascal, равно как и его предшественник — язык Pascal, а так же C и C++ относятся к строго типизированным языкам программирования. Типизированные языки, в отличие от нетипизированных (начиная с BASIC и заканчивая различными языками сценариев вроде JavaScript), имеют то преимущество, что уже на этапе синтаксической проверки кода компилятор не допустит заведомо неверных операций. К примеру, вы не сможете сложить строку с числом. Кроме того, типизация положительно сказывается на быстродействии программ: благодаря тому, что компилятор заранее «знает», что за данные ему следует обработать в каждом конкретном случае, он может подбирать оптимальные инструкции при генерации машинного кода.
Теперь рассмотрим собственно типы данных, имеющиеся в Object Pascal. Прежде всего отметим, что они бывают простыми (числа, символы), структурными (массивы, записи), процедурными и вариантными. Знакомство со структурными и процедурными типами мы отложим до соответствующей главы, а пока что перечислим категории простых типов данных:
- Целочисленные (Integer);
- Вещественные (Real);
- Булевы (Boolean);
- Символьные (Character);
- Строковые (String).
Практически в каждой категории имеется несколько типов данных. В свою очередь это так же вызвано необходимостью более эффективного использования ресурсов компьютера. Например, если для какой-то переменной достаточно диапазона значений от 1 до 100, то зачем для нее выделять память, достаточную для хранения миллиардных значений? Так что начнем подробное с целочисленных типов, как наиболее простых и наглядных.
Всего в современных версиях Delphi предусмотрено 7 различных типов данных для целых чисел, все они приведены в таблице 3.2.
Тип | Диапазон | Байт (бит) памяти | Отрицательные значения |
---|---|---|---|
Byte | от 0 до 255 | 1 (8) | Нет |
ShortInt | от -128 до 127 | 1 (8) | Да |
Word | от 0 до 65535 | 2 (16) | Нет |
SmallInt | от -32768 до 32767 | 2 (16) | Да |
LongWord | от 0 до 4294967295 | 4 (32) | Нет |
LongInt | от -2147483648 до 21474483647 | 4 (32) | Да |
Int64 | от -9223372036854775808 до 9223372036854775807 | 8 (64) | Да |
ПРИМЕЧАНИЕ
Здесь следует сразу оговориться про понимание памяти в программировании. Так, память считают в байтах. Каждый байт состоит из 8 бит. Бит — это минимальная единица информации, бит может принимать только 2 значения, 0 или 1. Каждая переменная, в зависимости от типа, занимает то или иное количество байт в памяти. Отметим так же, что 2 байта образуют слово (word), а 4 байта — двойное слово.
Помимо перечисленных основных типов, в Delphi имеется еще 2 автоматических целочисленных типа — Integer и Cardinal. Первое, в типичном случае, является синонимом для LingInt, хотя может быть приведено и к типу Int64. Например, если объявит переменную типа Integer и попробовать записать в нее значение, превышающее максимально допустимый размер для типа LongInt, то она автоматически преобразуется в Int64:
var x: integer; . x: = 21474483647; // здесь x имеет тип LongInt x: = x + 1;
Что касается Cardinal, то это — устаревшее определение для LongWord, вы сможете встретить его, если будете просматривать исходные коды, написанные во времена первых версий Delphi. Самым распространенным на практике целочисленным типом данных является Integer.
Перейдем к вещественным типам. Для них так же предусмотрены фиксированные типы (правда, в отличие от целочисленных, их не 7, а 6), и один автоматический. Рассмотрим основные типы в таблице 3.3.
Тип | Диапазон | Байт памяти | Точность |
---|---|---|---|
Real48 | от ±2.9*10^-39 до 1.7*10^38 | 6 | 11.дек |
Single | от ±1.5*10^-45 до 3.4*10^38 | 4 | 07.авг |
Double | от ±5.0*10^-324 до 1.7*10^308 | 8 | 15-16 |
Extended | от ±3.4*10^-4951 до 1.1*10^4932 | 10 | 19-20 |
Comp | от -2^63+1 до 2^63 -1 | 8 | 19-20 |
Currency | от -922337203685477.5808 до 922337203685477.5807 | 8 | 19-20 |
Имеется так же и автоматический тип — Real, введенный для совместимости с программами, написанными в Delphi 2 или 3. Сейчас тот тип, что был в ранних версиях Delphi, называется Real48 и практически не используется. Вместо него рекомендуется использовать такие типы, как Single или Double. Если же задать тип Real в программе, то он будет автоматически приведен к типу Double.
Нечто подобное можно сказать и про тип Comp — этот 64-разрядный вещественный тип данных изжил себя с момента появления целочисленного типа Int64, и присутствует лишь в целях совместимости со старым программным кодом.
Зато тип Currency активно используется, особенно при работе с базами данных. Как следует из его названия (Currency — валюта), данный тип предпочтительно использовать для финансовых расчетов. И хотя Currency относится к вещественным типам данных, компилятор обращается с ним как с целым, что позволяет добиться меньших ошибок при округлениях.
В целом, что касается целых и вещественных типов, достаточно запомнить всего две вещи: вещественным типам данных можно присваивать любые численные значения, а целочисленным — только целые:
var x: integer; y: double; . x := 5; y := 5.25; // обратите внимание, что дробная часть отделяется точкой y := x + y; // так делать можно x := x + y; // а так — нельзя, поскольку результатом должно быть целое
Булевы, или логические типы данных представлены в Delphi типами Boolean, ByteBool, WordBool и LongBool. Все они отличаются только размером памяти, выделяемым для хранения значения, причем значений может быть только 2 — false (ложь) и true (истина):
var x, y: Boolean; . x := true; y := false;
Основным типом является 1-байтовый Boolean (он же ByteBool), 2-байтовый WordBool и 4-байтовый LongBool предусмотрены лишь для совместимости в целях взаимодействия с другими языками и платформами.
Что касается символьных типов данных, то в Delphi предусмотрено 2 их типа — ANSIChar и WideChar. Первый является однобайтовым и может хранить в себе 1 символ из множества символов ANSI, коих насчитывается 256. Второй же тип является 2-байтовым и предназначен для хранения 2-байтовых же символов Unicode. Как и в других случаях, Delphi имеет синоним для символьных типов — Char, который на сегодня является аналогом ANSIChar. Что касается присвоения значений, то обычные символы (буквы и цифры) присваивают переменным символьного типа как есть, лишь заключая их в одиночные кавычки. А специальные символы, например, возврат каретки (Enter) назначают при помощи их номера в таблице ANSI, и выделяют знаком решетки:
var x, y: Char; // x и y получают тип ANSIChar . x := a; // обычные символы y := #13; // возврат каретки в таблице ANSI имеет номер 13
Наконец, еще одним, причем, в общем-то, уже не совсем простым типом данных являются строки. Строковые типы данных отличаются от символьных тем, что могут хранить не единичный символ, а множество символов. В Delphi имеется 3 типа строк: ShortString, AnsiString и WideString. Первый тип строк — ShortString — достался в наследство от языка Pascal и 16-битной Delphi 1.0. Такие строки могут иметь не более 255 символов, и занимают от 2 до 256 байт памяти, в зависимости от размера: Что касается современных строковых типов — AnsiString и WideString, то они могут иметь практически неограниченную длину (AnsiString — до 2 млрд. символов, WideString — до 1 млрд.) и занимать, соответственно, от 4 байт до 2 Гигабайт памяти. При этом по аналогии с символьными типами, тип AnsiString предназначен для хранения обычных строк, а WideString — для строк в формате Unicode. Ну и еще один тип строк — String является синонимом для типа AnsiString:
var str1: ShortString; // короткая строка var str2: AnsiString; // длинная строка var str3: String; // тоже длинная строка . str1 := Начало.; // Строковые значения заключаются в одинарные кавычки str2 := Конец.; str3 := str1 + str2; // получим длинную строку, содержащую Начало.Конец.
В целом, несмотря на кажущееся разнообразие типов данных, на практике чаще всего ограничиваются всего лишь 5-6 основными типами. Это: Integer, Double, Boolean, Char, String, и иногда — еще и Currency.
Данные и значения
Очевидно, что сами по себе типы данных ничего не означают. Главное их предназначение — хранить те или иные значения, или данные. Так, для хранения числовых данных применяют целочисленные или вещественные типы, в зависимости от того, какого типа числа следует использовать. На практике это означает, что будут применяться типы Integer и Double.
ПРИМЕЧАНИЕ
Хотя вещественные типы и являются более универсальными, использовать их надо только при реальной необходимости, поскольку они гораздо менее удобны компьютеру для вычислений. Говоря конкретнее, математические операции над вещественными числами выполняются гораздо медленнее, чем над целыми, а ряд иных операций (например, побитовые, инкремент или декремент) вообще недопустимы.
Строковые данные требуют для своего хранения строковых же типов, т.е. String. Исключение может составить тот случай, когда следует сохранить один и только один символ — в таком случае предпочтительно (а иногда — необходимо) использовать тип Char. Ну а в том случае, если требуется привести какое-то значение к однозначному выводу, используют логический тип Boolean. Например, если сравнивать одно число с другим на предмет того, является ли оно больше, то результатом сравнения будет либо «да», либо «нет», т.е. true или false:
var x: boolean; . x := 5 > 6; // получаем false, т.к. 5 не больше 6
Важно отметить, что переменные одного типа могут хранить данные только такого же типа. Кроме того, произведение каких-либо манипуляций над данными возможно только в том случае, если они относятся к одной категории. Исключение составляют разве что такие пары, как строки-символы и вещественные-целые числа. При этом результирующая переменная должна иметь тип строки для первой пары и вещественного числа — для второй. Впрочем, подробнее все это будет рассмотрено в контексте операций языка Object Pascal.
Операции и их типы
В любом языке программирования имеются знаки операций. Кроме того, некоторые ключевые слова, например такие, как div или mod также обозначают операции. Все операции в Object Pascal можно разделить на следующие типы: логические, арифметические, логические, операции присвоения и отношения, а так же специальные операции. Для их обозначения используются математические символы или ключевые слова. Участвующие в операциях значения (переменные) называются операндами. При этом та или иная операция может работать с операндами определенного типа. А результатом может быть данные как такого же типа, та и другого (например, для того же сравнения).
Начнем ознакомление с арифметических операций, как наиболее распространенных (не только и не столько в программировании, сколько в реальной жизни). С полным их перечнем, а так же с типами исходных и результирующих данных можно ознакомиться в таблице 3.4.
Операция | Название, тип | Описание | Операнды | Результат |
---|---|---|---|---|
+ | Сложение, бинарная | Возвращает сумму левого и правого операндов | integer, real | integer, real |
— | Вычитание, бинарная | Возвращает разницу левого и правого операндов | integer, real | integer, real |
* | Умножение, бинарная | Возвращает произведение левого операнда на правый операнд | integer, real | integer, real |
/ | Деление, бинарная | Возвращает результат деления левого операнда на правый операнд. Результат может быть дробным | integer, real | real |
mod | Остаток от деления, бинарная | Возвращает остаток от деления левого операнда на правый операнд | integer | integer |
div | Деление нацело, бинарная | Возвращает целую часть числа, получившуюся в результате деления | integer | integer |
— | Унарный минус | Возвращает число, противоположное операнду | integer, real | integer, real |
+ | Унарный плюс | Явно указывает знак числа | integer, real | integer, real |
Можно отметить, что практически для всех арифметических операций результатом будут данные того же типа, что и операнды. Единственное исключение — это операция деления, результатом которой всегда будет вещественное число.
Другой распространенный тип операций — логические. В Object Pascal имеются все 4 типа логических операций: не, и, или, исключающее или (таблица 3.5).
Операция | Название | Описание |
---|---|---|
not | Логическое отрицание (НЕ) | Возвращает false, если выражение может быть приведено к истине, в противном случае возвращает true |
and | Логическое (И) | Возвращает true, когда оба выражения истинны. В противном случае возвращает false |
or | Логическое (ИЛИ) | Возвращает true, когда хотя бы одно из выражений истинно. В противном случае возвращает false |
xor | Логическое (исключающее ИЛИ) | Возвращает true, когда только одно из выражений истинно. В противном случае возвращает false |
Варианты возвращаемых значений для логических операций приводятся ниже:
not true // возвращает false not false // возвращает true true and true // возвращает true true and false // возвращает false false and false // возвращает false true or true // возвращает true true or false // возвращает true false or false // возвращает false true xor true // возвращает false true xor false // возвращает true false xor false // возвращает false
Логическому сравнению подлежат не только булевские значения, но и любые другие выражения, которые могут быть к ним приведены. Например, выражение «3=4» может быть использовано в качестве логически сравниваемой единицы, поскольку результатом его оценки будет булево значение ложь (false).
Те же самые знаки операций, что используются в логических операциях, задействованы и в другом типе операций — побитовых. Побитовые операции выполняются над числами, представленными в двоичном виде (т.е. только нули и единицы). Однако сами операнды могут быть десятичными, шестнадцатеричными, или восьмеричными целыми числами. Например, десятичное число 5 представляется как двоичное 101, десятичное 6 — как 110, а шестнадцатеричное F3 — как двоичное 11110011.
Хотя побитовые операции выполняются над двоичными данными, возвращаемые значения являются стандартными числами. Список всех побитовых операций приводится в таблице 3.6.
Операция | Название | Описание |
---|---|---|
and | Побитовое И | Возвращает число, являющееся результатом побитового сравнения «И» |
or | Побитовое ИЛИ | Возвращает число, являющееся результатом побитового сравнения «включающее ИЛИ» |
xor | Побитовое исключающее ИЛИ | Возвращает число, являющееся результатом побитового сравнения «исключающее ИЛИ» |
not | Побитовое НЕ | Возвращает число, с битами, расположенными в обратном порядке |
shl | Сдвиг влево | Сдвигает первый операнд влево на число разрядов, заданных вторым операндом. Освобождающиеся правые биты заполняются нулями |
shr | Сдвиг вправо | Сдвигает первый операнд вправо на число разрядов, заданных вторым операндом. Освобождающиеся левые биты отбрасываются |
Чтобы явно представить себе, как работают побитовые операции, обратимся к следующему примеру. Допустим, имеется 2 переменных — x и y:
var x, y: integer; . x := 3; y := 5;
В двоичном представлении число 3 будет выглядеть как 0011, а 5 — как 0101. Теперь посмотрим, какие результаты даст каждая из побитовых операций сравнения и операции отрицания над этими числами:
x or y // Получим 7: 0011 | 0101 = 0111 x and y // Получим 1: 0011 & 0101 = 0001 x xor y // Получим 6: 0011 ^ 0101 = 0110 not x // Получим 12:
Что касается операций побитового сдвига, то они дадут следующие результаты:
y shl 2 // Получим 20: 0101 shl 2 = 10100 y shr 2 // Получим 1: 0101 shr 2 = 01 -y shr 2 // Получим -2: -0101 shr 2 = -10
Теперь нам остается исследовать еще один тип операций — операции сравнения. Эти операции всегда требуют наличия двух операндов: они сравнивают значения левого и правого операндов, и возвращают результат сравнения в виде логического значения, которое может принимать значение false или true (ложь или истина). Все имеющиеся в Object Pascal операции сравнения приведены в таблице 3.7.
Операция | Название | Описание | Пример, дающий true | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
= | Равно | Возвращает истину (true), когда левый и правый операнды равны. | 1=1 | ||||||||||||
<> | Не равно | Возвращает истину, когда левый и правый операнды не равны. | 1<>2 | ||||||||||||
> | Больше | Возвращает истину, когда левый операнд больше правого. | 2>1 | ||||||||||||
= | Больше или равно | Возвращает истину, когда левый операнд больше правого или равен ему. | 1>=0; 1>>1 | ||||||||||||
var x: Char; z: Boolean; . x := ‘b’; z := x in [a..d];
В данном случае в качестве результата (z) мы получим истину, поскольку символ b является членом указанного множества [a..d], в которое входят символы a, b, c и d. Наконец, в Object Pascal имеется еще 2 операции — as и is. Они служат для приведения типа и проверки типа, соответственно. Например, если мы хотим проверить, является ли некая переменная «x» целым, то можно написать такое выражение: b := x is Integer; // b получит значение true, если x — целое Ну а операция as используется для приведения данных одного типа к другому, причем, преимущественно, при работе с объектами: При использовании операции as следует быть осторожным, поскольку приведение одного типа к другому возможно далеко не всегда. Выражения и приоритет операцийКак нам уже известно, выражения — это, прежде всего, набор данных, переменных и операторов. Развивая это определение, можно сказать, что выражения состоят из значений и операций над ними, например: a := b + c; d := e * f; g := a — d div 2; Так же, как и в обычной математике, при составлении выражений в Object Pascal, следует учитывать приоритет выполнения операций. Например, операции умножения или деления должны выполняться раньше, чем сложение и вычитание. Так, в 3-й строке из приведенных выше примеров выражений, согласно математическим правилам, сначала выполняется операция деления нацело (d div 2), затем результат этой операции вычитается из a, и итоговое значение присваивается переменной g. Все те же правила действуют и в программировании, но поскольку перечень операций не ограничивается арифметическими, а в рамках одного выражения могут быть использованы различные типы операций, то было бы неплохо внести полную ясность в этот вопрос. Прежде всего, приоритет выполнения определяется принадлежностью конкретной операции к тому или иному типу. Так, первыми выполняются унарные операции, затем — операции умножения (включая деление и побитовые), далее обрабатываются сложение и вычитание (опять, таки включая побитовые or и xor), и, наконец, в последнюю очередь — операции отношения. Для удобства представим все операции, классифицированные по уровню приоритета, в виде таблицы (табл. 3.8).
В тех случаях, когда порядок следования операций, задаваемый их приоритетом, следует изменить, применяют круглые скобки. Например, если мы имеем выражение: И нам требуется сначала выполнить вычитание, то достаточно заключить участвующие в операции вычитания операнды в круглые скобки: Таким образом, сначала здесь из a вычитается d, и лишь затем производится операция деления нацело. Структура программыТеперь, когда мы уже знаем об основных языковых конструкциях языка — «словах» (ключевые слова, переменные, операторы и т.д.) и выражениях, рассмотрим, как все это объединяется в общий код программы. Итак, программа состоит из: заголовка, за которым следуют список подключаемых модулей, объявления меток, констант, типов данных и переменных, описания процедур и функций. Вслед за этой заголовочной частью располагаются собственно программные инструкции. На практике все это выглядит таким образом: program ; uses ; label ; const ; type ; var ; ; begin ; end. В структуре той или иной программы часть этих разделов может быть опущена за ненадобностью. Обязательными являются всего 3 ключевых слова — program (вместе с названием), а так же begin и end. Разумеется, между begin и end должны находиться какие-либо инструкции. Так, возвращаясь к примеру «Hello, World», в нем можно обнаружить лишь название программы и блок инструкций: program hello; // название <$APPTYPE CONSOLE>//это указание компилятору к коду программы не относится begin // начало исполняемого кода write(Hello, World!); // инструкции readln; end. // конец программы ПРИМЕЧАНИЕ Так же следует отметить, что те или иные модули могут повторяться, причем неоднократно. Например, объявления констант или переменных могут чередоваться, равно как и описание типов, функций, или процедур. Другое дело, что с точки зрения собственного удобства следует следить за тем, чтобы все это было педантично разложено по своим местам, поскольку в противном случае много времени будет уходить на поиск того или иного объявления. Рассмотренная нами структура характерна для программ на Pascal в целом, в том числе и для Windows-приложений, разрабатываемых визуальными средствами. Структура же отдельных модулей, представляющих собой описание того или иного окна, имеет ряд отличий. В общих чертах ее можно представить следующим образом: начинается такой модуль с ключевого слова unit, после которого следуют секции interface и implementation. Иногда могут быть также использованы еще 2 секции — initialization и finalization. Ну а завершается все это, разумеется, ключевым словом end с точкой: unit ; interface implementation ; initialization ; finalization ; end. При создании новых форм в среде Delphi весь необходимый код создается автоматически, разве что опциональные (и довольно редко используемые на практике) секции загрузки или завершения, при необходимости, придется дописывать самостоятельно. Завершая эту тему, затронем еще один аспект структуры программ — подпрограммы. В качестве подпрограммы понимается блок инструкций, предназначенный для решения какой-либо частной задачи в рамках самой программы. Такие блоки в Object Pascal могут просто выполняться — в таком случае они называются процедурами, либо выполняться и возвращать какой-либо результат — в таком случае они называются функциями. И в том, и в другом случае, они могут принимать какие-либо значения для обработки. Эти значения называются аргументами. В целом же синтаксис обращения к процедуре или функции выглядит следующим образом: Подробнее об этом будет рассказано в соответствующей главе, пока же отметим для себя сам факт их существования, поскольку очень скоро нам придется столкнуться с рядом процедур и функций, реализованных в самом языке Object Pascal. Исключительные ситуации и интерфейсы в Delphi
Большинство разработчиков на Delphi создают довольно сложные программы. Сложная программа подразумевает разносторонее взаимодействие с операционной системой и приложениями операционной системы. Любое из этих взаимодействий может завершиться неправильно. Примеров этого можно привести очень много, от банального деления на ноль, до открытия несуществующего файла. Обычно для обхода таких ситуаций разработчику приходится вводить многочисленные проверки. Но любой разработчик просто не в состоянии рассмотреть все ситуации, которые могут возникнуть у его программы при взаимодействии с операционной системой и другими приложениями. Именно для таких непредвиденных событий была придумана структурированная обработка исключительных ситуаций. Первоначально она была создана для разработчиков под Windows NT, впоследствии структурированная обработка получила поддержку и в операционных системах Windows 9x. Большинство современных программных сред для создания приложений под Windows поддерживают обработку исключительных ситуаций. Не осталась в стороне и среда Delphi. Обработка исключений была введена уже в Delphi 1.0, но, только начиная с Delphi 2.0, исключения стали частью Win32 API. Если при выполнении кода, размещенного в разделе try, генерируется исключение, то выполнение этого раздела прекращается и управление передается коду, размещенному в разделе except. Раздел except может использоваться двумя способами. Во-первых, в нем могут располагаться любые операторы, кроме обработчиков исключений, начинающихся с приставки on. Это и операторы сообщения об ошибке, и команды, позволяющие освобождать системные ресурсы, а также другие операторы и команды. Во-вторых, раздел except используется для обработки исключений. В этом случае в него могут включаться только операторы обработки исключений. Если среди обработчиков встретился обработчик, соответствующий сгенерированному исключению, то выполняется оператор этого обработчика, исключение разрушается и управление передается коду, расположенному после оператора on Exception do. Раздел, расположенный после ключевого слова else, служит для обработки любых исключений, не описанных в разделе except. Этот раздел не является обязательным. Если при обработке исключительной ситуации не будет найден подходящий обработчик, то произойдет обработка системным обработчиком исключений. Листинг 1.7 Итак, как можно видеть из приведенного выше примера, для обработки разных исключений служат разные операторы. Рассмотрим более подробно оператор обработки on .. do . Данный оператор находится внутри раздела except и может иметь две формы (листинг 1.8). Листинг 1.8 Этот оператор обрабатывает только тот класс исключений, который в нем указан. При указании родительского (базового) класса, все классы исключений — потомки данного класса — также будут обработаны. Для обработки всех исключений можно обратиться к базовому классу всех исключений: Exception. После обработки исключения оно разрушается. Вторая форма оператора on .. do отличается от первой тем, что данному исключению можно временно присвоить имя и обращаться к свойствам исключения. Обращаться к свойствам исключения можно с помощью конструкции . . Посмотрим листинг 1.9. Листинг 1.9 В приведенном примере мы присваиваем исключению EInvalidOperation временное имя Е. Затем в окне сообщения выводим текст ошибки E.Message, выдаваемый Delphi по умолчанию (если бы не было нашего обработчика ошибки). Примечание Иногда, бывает необходимо, чтобы после обработки исключительной ситуации своим кодом вызывался стандартный обработчик ошибки. Например, в случае возникновения некоторой ошибки вы хотите, чтобы приложение сообщало пользователю какую-либо информацию, а затем передавало управление стандартному обработчику ошибок. Как вы уже знаете, после обработки исключения вашим кодом, исключение уничтожается. Для того чтобы самостоятельно вызвать снова это исключение, можно воспользоваться регенерацией исключений. Для регенерации исключения служит команда raise. Рассмотрим листинг 1.10. После выполнения операторов обработки исключения, написанных программистом, выполняется команда raise, которая снова принудительно вызывает это исключение, после чего управление передается стандартному обработчику исключений. Листинг 1.11 Следующая конструкция try .. finally служит для защиты кода, записанного в разделе finally от исключительных ситуаций, которые в силу каких-либо причин могут происходить в разделе try. Синтаксис этой конструкции представлен на листинге 1.12. Итак, операторы, которые размещены после ключевого слова finally , будут выполняться в любом случае, была сгенерирована исключительная ситуация или нет. Если в разделе try была сгенерирована исключительная ситуация, то управление немедленно передается разделу finally . Также, если исключительной ситуации в разделе, try не было, блок finally будет выполняться. Даже если в разделе finally произойдет ошибка, выполнение операторов этого раздела будет продолжено до конца. В конструкции try .. finally не происходит обработка исключений, она используется в основмом для освобождения ресурсов памяти, закрытия ненужных файлов и других операций освобождения ресурсов. Таким образом, в данной конструкции нуждаются операции с файлами, памятью, ресурсами Windows и объектами. Примечание Для того чтобы сгенерировать молчаливое исключение, можно вызвать процедуру Abort. Она автоматически сгенерирует исключение EAbort, которое прервет текущую операцию без вывода сведения об ошибке на экран. Рассмотрим пример. Пусть форма содержит пустой список (ListBoxi) и кнопку (Button1). Запишем в обработчик события кнопки onclick следующий код: Листинг 1.13 В результате работы программы, после нажатия кнопки Button1, в список будет добавлено семь строк с номерами от 1 до 7. Рис. 1.24. Вложенные блоки в обработчике исключений (а) и в конструкции защиты кода (б) Ошибка доступа к файлу или устройству ввода/вывода Большинство исключений ввода/вывода связано с кодом ошибки, возвращаемом Windows при обращении к файлу Ошибка использования динамической памяти Ошибки кучи возникают при недостатке памяти или когда в приложении присутствует указатель на область памяти вне кучи Целочисленные математические операции Неправильное действие с выражением целого типа Ошибки включают в себя: деление на ноль, переполнение, выход за пределы диапазона и др. Математические операции с плавающей точкой Неправильное действие с выражением вещественного типа Ошибки с вещественными числами могут исходить от математического сопроцессора или программного эмулятора. Ошибки включают в себя: неправильные инструкции, деление на ноль, переполнение и др. Неправильная работа с классами при помощи операции as Объекты могут работать только с совместимыми объектами Неправильное преобразование типов Функции преобразования типов (IntToStr, StrToInt И др.) генерируют эту ошибку в случае невозможности преобразования Аппаратные ошибки указывают, что или процессор, или пользователь сгенерировал ошибку: доступа, переполнения стека или другую Неправильное использование типа Ошибка возникает в выражениях, где не может использоваться тип |
Рассмотрим теперь иерархию классов исключений .более подробно:
— базовый класс исключений — исключение для намеренного прерывания вычислений — попытка вызова абстрактного метода — ошибка доступа к памяти — ошибка при работе с массивами — ошибка при проверке истинности — ошибка доступа к массиву булевых величин TBits — ошибка построения кэша — ошибка регистрации или переименования компонента — нажатие пользователем клавиш + при выполнении консольного приложения — ошибка преобразования строк (объектов) — ошибка работы с базами данных — ошибка в наборе данных клиента — ошибка обновления данных компонента — генерируется компонентом TQuery при попытке открыть запрос без select — ошибка при обновлении В TProvider — ошибка ввода даты или времени — ошибка формата данных в кубе решений — ошибочный индекс в задании размерности в кубе решений — ошибка ввода/вывода в файл — базовый класс исключений целочисленных математических операций — ошибка деления на ноль — значение или индекс вне допустимого диапазона — ошибочное преобразование типов as к интерфейсу — нераспознаваемый графический файл — ошибка при операциях с графикой — ошибка при работе с сеткой (Grid) — ошибочная операция с компонентом — ошибка при операциях с указателем — ошибка при работе со списком — ошибка выделения памяти для куба решений — базовый класс исключений операций с плавающей запятой — недопустимое значение параметра при обращении к математической функции — потеря значащих разрядов — ошибка деления на ноль — ошибка доступа к устройствам через драйвер MCI (Media Control Interface) — ошибка при работе с элементами меню — ошибка при связывании приложения с элементом ActiveX — низкоуровневая ошибка OLE — ошибка интерфейса OLE IDispatch — ошибка OLE, связанная со свойством или методом — ошибка при работе с Tout line — ошибка распределения памяти — ошибка создания обработчика Windows — исключение, генерируемое при загрузке или использовании пакета — ошибка преобразования текста описания формы в двоичный формат — ошибка выполнения инструкции процессора из-за нехватки привилегий — ошибка записи с помощью OLE значения свойства, предназначенного только для чтения — ошибка чтения с помощью OLE значения свойства, предназначенного только для записи — ошибка при задании значения свойства — ошибка при работе с реестром Windows — ошибка задания типа сервера (компонент TReport не может соединиться с базой данных) — ошибка загрузки файла ресурсов (*.DFM или *.RES) во время создания приложения — переполнение стека — базовый класс исключений ошибок потоков — ошибка создания файла — ошибка открытия файла — базовый класс исключений файловых потоков — ошибка чтения заданного числа байт — ошибка записи заданного числа байт — ошибка связи компонента с приложением — ошибка чтения файла ресурсов — не найден метод — ошибка доступа к окну списка — ошибка многопоточного приложения — ошибка индекса при работе с TTreeview — ошибка при работе с типом данных variant — внутренняя ошибка Windows |
Итак, класс Exception является базовым классом всех исключений в Delphi. Все вышеописанные классы являются прямыми или косвенными наследниками класса Exception. При создании собственных новых классов исключений необходимо использовать класс Exception как родительский. Только в этом случае Delphi гарантированно распознает и обработает новый класс как исключение. В свою очередь, класс Exception является прямым наследником базового класса TObject, и наследует все его функции. В отличие от
других классов, классы исключений начинаются не с буквы т, а с буквы Е. При создании собственных классов исключений можно называть их по своему усмотрению, не обязательно начиная с буквы Е (но это считается плохим стилем программирования).
Создание собственных исключений
Для создания собственных типов исключений необходимо знать, как определить тип объекта исключения, а также как вызвать исключение.
Так как исключение является объектом Delphi, то определение нового типа исключения так же nporfro, как определение объекта нового типа. Теоретически возможно вызывать любой объект как объект исключения, но стандартные обработчики исключений работают только с теми объектами, предками которых являются Exception или потомки Exception.
В качестве примера создания собственного типа исключения рассмотрим следующее определение:
type
EMyException = class(Exception);
Теперь, если вы вызовете исключение EMyException, но не напишете обработчик для него, то произойдет вызов стандартного обработчика для Exception. Так как стандартный обработчик для Exception показывает имя вызванного исключения, вы можете увидеть имя вашего нового исключения.
Для вызова созданного исключения используйте команду raise. Для примера рассмотрим типичную задачу проверки введенного пользователем пароля:
type
EPasswordlnval >
После определения нового типа исключения EpasswordInwalid вы можете вызвать это исключение в любом месте программы:
if Password <> CorrectPassword then raise EPasswordlnvalidCreate(‘Введен неправильный пароль’);
Вызов созданного исключения производится по его имени.
Интерфейсы и их совместное использование классами
Ключевое слово Delphi interface позволяет создавать и использовать интерфейсы в ваших приложениях. Интерфейсы служат для расширения модели наследования в VCL, позволяя одному классу принадлежать нескольким
интерфейсам, а также нескольким классам — наследникам различных базовых классов использовать один интерфейс. Интерфейсы полезны в тех случаях, когда наборы операций, такие как потоки, используются большим количеством объектов.
Таким образом, интерфейсы — это средства для обеспечения взаимодействия между разными объектами.
Интерфейсы являются фундаментом для технологий компонентной объектной модели (СОМ) и CORBA.
Интерфейсы похожи на классы, которые содержат в себе только абстрактные методы и четкие определения их функциональности. Определение метода интерфейса включает в себя параметры и типы параметров, возвращаемый тип, а также ожидаемое поведение. Методы интерфейса семантически или логически связаны с отражением цели интерфейса. Существует соглашение об интерфейсах, гласящее, что каждый интерфейс должен быть назван в соответствии с задачей, которую он будет выполнять. Например, интерфейс iMalloc предназначен для распределения, освобождения и управления памятью. Аналогично, интерфейс IPersist может использоваться как базовый интерфейс для потомков, каждый из которых определяет специфичные прототипы методов для загрузки и сохранения состояния объектов в память, поток или в файл. Приведем простой пример объявления интерфейса (листинг 1.14):
Листинг 1.14
type
IEdit = interface
procedure Copy; stdcall;
procedure Cut; stdcall;
procedure Paste; stdcall;
function Undo: Boolean; stdcall;
end;
Примечание
Для тех читателей, которые имеют слабое представление о создании компонентов и новых классов, советуем прочитать вторую часть книги, посвященную созданию собственных и модификации существующих компонентов.
Нельзя создать экземпляр интерфейса при помощи интерфейса. Для получения экземпляра интерфейса вам нужно объявить его в классе, содержащем данный интерфейс. Таким образом, нужно определить класс, который содержит необходимый интерфейс в списке своих родителей (листинг 1.15).
Листинг 1.15
TEditor = class(TInterfacedObject, lEdit)
procedure Copy; stdcall;
procedure Cut; stdcall;
procedure Paste; stdcall;
function Undo: Boolean; stdcall;
end;
Как уже было отмечено выше, использование интерфейсов позволяет нескольким классам использовать один интерфейс, игнорируя требование наличия одного базового класса-предка. Следует запомнить, что интерфейс — это тип с управляемым временем жизни, т. е., он автоматически, при инициализации, принимает значение nil, обладает счетчиком ссылок и автоматически уничтожается, при выходе за пределы своей области видимости.
Интерфейс IUnknown
По аналогии с наследованием классов, предком которых является базовый класс TObject, все интерфейсы — это прямые или косвенные наследники интерфейса IUnknown. Этот базовый интерфейс описан в модуле System следующим образом (листинг 1.16):
Листинг 1.16
type
IUnknown = interface
[‘< 00000000-0000-0000-С000-000000000046>‘]
function Querylnterfасе(const IID: TGUID; out Obj): Integer; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;7
end;
Синтаксис описания интерфейса похож на описание класса. Главное отличие заключается в том, что интерфейс может быть связан с глобальным уникальным идентификатором (Global Unique Identifier, GUID).
GUID — это 128-разрядное целое число, которое используется для уникальной идентификации интерфейсов. Так как в 128-ми разрядах можно закодировать большое количество чисел, GUID практически гарантирует глобальную уникальность идентификатора интерфейса. То есть, практически невозможно, чтобы на двух компьютерах GUID совпал. Алгоритм генерации GUID основан на аппаратной части компьютера (частота процессора, номер сетевой карты и т. д.). В результате работы алгоритма, который может быть реализован с помощью функции API CocreateGUID (), получается запись типа TGUID. Эту запись можно определить в виде строки следующего формата:
‘<хххххххх-хххх-хххх-хххх-хххххххххххх>‘
В дальнейшем, при рассмотрении СОМ, мы увидим, что каждый интерфейс или класс СОМ имеет собственный GUID. Для интерфейсов — это идентификатор интерфейса (Interface ID, IID), а для класса — идентификатор класса (Class ID, CLSID).
Для создания нового GUID в среде Delphi достаточно нажать комбинацию клавиш + + в окне редактора кода.
Итак, интерфейс lunknown поддерживает три метода, которые наследуются всеми интерфейсами:
— QueryInterface() — используется для создания запроса, поддерживается ли данный интерфейс и если ответ положителен, то метод возвращает указатель на него. Для примера, предположим, что имеется некоторый объект object, который поддерживает несколько интерфейсов interface1, interface2 и др. Для получения указателя на интерфейс interface2, объекта Object, вам нужно вызвать метод Interface2.Query Interface О;
— _AddRef () — используется, когда получен указатель на данный интерфейс и вы хотите работать с этим указателем. Метод _AddRef() обязательно должен заканчиваться вызовом метода _Release ();
— _Release () — данный метод применяется для завершения работы с интерфейсом.
Интерфейсы являются фундаментальными элементами таких распределенных объектных моделей, как СОМ и CORBA.
Более подробно интерфейс lunknown мы рассмотрим в третьей части книги, посвященной использованию технологий СОМ и ActiveX.
Класс TlnterfacedObject
В VCL Delphi определен класс TlnterfacedObject, который служит базовым классом для объектов интерфейса. Данный класс определен в модуле Delphi system следующим образом (листинг 1.17):
Листинг 1.17.
type
TlnterfacedObject = class(TObject, IUnknown) private
FRefCount: Integer;
protected
function Querylnterface(const IID: TGUID; out Obj): Integer; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall; public
property RefCount: Integer read FRefCount;
end;
Как мы видим, данный класс в качестве родителей имеет класс TObject и интерфейс lunknown. Класс Tinterfacedobject позволяет достаточно легко создавать классы, поддерживающие интерфейсы. Например,
type
TMyObjInterfaced = class(TInterfacedObject, IPaint)
end;
На вышеприведенном примере мы определяем новый класс TMyObj interfaced, который является прямым потомком класса Tinterfacedobject и поддерживает некий интерфейс IPaint.
Использование оператора as
Объекты, поддерживающие интерфейсы, могут использовать оператор as для динамического присоединения интерфейса. Например,
procedurePaintObjecta(P: TInterfacedObject) var
X: IPaint; begin
X := P as IPaint;
<операторы>
end;
В этом примере переменная Р имеет тип Tinterfacedobject. Данная переменная может быть назначена переменной х, как ссылка на интерфейс IPaint.. Для такого назначения компилятор генерирует код для вызова метода Querylnterface, относяшегося к Интерфейсу IUnknown переменной Р. Подобное назначение возможно, даже если Р не поддерживает данный интерфейс. То есть, компилятор не выдаст ошибку при таком назначении.
Во время выполнения вышеприведенного примера либо успешно происходит присваивание
Х:= Р as IPaint;
либо генерируется исключительная ситуация.
При использовании оператора as вы должны выполнять следующие требования:
— при объявлении интерфейса, явно объявляйте в качестве предка интерфейс lunknown. Так как только в этом случае вы сможете воспользоваться оператором аs;
— если вы используете оператор as для интерфейса, данный интерфейс должен иметь свой IID. Напомним, что для создания нового IID достаточно, находясь в редакторе кода, использовать комбинацию клавиш + + .
Использование ключевого слова implements
Многие классы VCL Delphi имеют в качестве некоторых своих свойств объекты. Кроме того, вы можете использовать в качестве свойств класса интерфейсы. В том случае, когда свойство имеет тип интерфейса, то вы можете использовать ключевое слово implements для определения методов, которые данный интерфейс передает объекту. По умолчанию, ключевое слово implements передает все методы интерфейса. Тем не менее, вы можете самостоятельно определить список тех методов интерфейса, которые передаются объекту.
На приведенном ниже листинге 1.18 представлен пример использования ключевого слова implements при создании объекта адаптера цвета, предназначенного для преобразования восьмибитного значения цвета RGB.
Листинг 1.18
unit cadapt;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
IRGBSbit = interface
[‘
function Red: Byte;
function Green: Byte;
function Blue: Byte;
end;
IColorRef = interface
[41d76360b-f4f5-lldl-87d4-00c04fbl7199>’] function Color: Integer; end;
TRGBSColorRefAdapter = class(TInterfacedObject, IRGBSbit, IColorRef) private
FRGBSbit: IRGBSbit;
FPalRelative: Boolean; public
constructor Create(rgb: IRGBSbit);
property RGBSIntf: IRGBSbit read FRGBSbit implements IRGBSbit;
property PalRelative: Boolean read FPalRelative write-FPalRelative;
function Color: Integer; end;
implementation
constructor TRGBSColorRefAdapter.Create(rgb: IRGBSbit);
begin
FRGBSbit := rgb; end;
function TRGBSColorRefAdapter.Color: Integer;
begin
if FPalRelative then
Result := PaletteRGB(RGBSIntf.Red, RGBSIntf.Green, RGBSIntf.Blue) else
Result := RGB(RGBSIntf.Red, RGBSIntf.Green, RGBSIntf.Blue);
end;
end.
Использование интерфейсов в распределенных приложениях
Интерфейсы являются фундаментальным элементом распределенных объектных моделей СОМ и CORBA (более подробно о моделях читайте в третьей части книги). Delphi обеспечивает базовые классы для этих технологий, которые расширяют возможности объекта TInterfacedObject.
Классы СОМ добавляют возможности использования фабрик классов и идентификаторов классов (CLSID). Фабрики классов отвечают за создание экземпляров классов посредством CLSID. В свою очередь, CLSID используются для регистрации и манипуляции классами СОМ. Классы СОМ, которые обладают и фабрикой класса, и идентификатором класса, называются CoClasses. CoClasses имеют преимущество перед Querylnterface по части поддержки новых версий интерфейсов. Новые версии старых интерфейсов автоматически становятся доступными для программ-клиентов. В приложе ниях СОМ разработчик может вносить правку в код интерфейса для улучшения работы приложения, не изменяя клиентской части кода.
Другая распределенная технология называется CORBA (Архитектура Брокера Общих Объектных Запросов, Common Object Request Broker Architecture). Описание данной технологии не входит в эту книгу, заметим только, что она позволяет создавать приложения для взаимодействия с различными аппаратными или программными платформами. Так, клиентское приложение CORBA, работающее в операционной системе Windows 98, также легко будет работать с сервером приложений операционной системы UNIX.
Программирование на языке Delphi. Глава 2. Основы языка Delphi. Часть 1
Оглавление
В основе среды Borland Delphi лежит одноименный язык программирования — Delphi, ранее известный как Object Pascal. При разработке программы среда Delphi выполняет свою часть работы — создает пользовательский интерфейс согласно вашему дизайну, а вы выполняете свою часть — пишите обработчики событий на языке Delphi. Объем вашей работы зависит от программы: чем сложнее алгоритм, тем тяжелее ваш труд. Необходимо заранее усвоить, что невозможно заставить средство разработки делать всю работу за вас. Некоторые задачи среда Delphi действительно полностью берет на себя, например создание простейшей программы для просмотра базы данных. Однако большинство задач не вписываются в стандартные схемы — вам могут понадобиться специализированные компоненты, которых нет в палитре компонентов, или для задачи может не оказаться готового решения, и вы вынуждены будете решать ее старым дедовским способом — с помощью операторов языка Delphi. Поэтому мы настоятельно рекомендуем вам не игнорировать эту главу, поскольку на практике вы не избежите программирования. Мы решили изложить язык в одной главе, не размазывая его по всей книге, чтобы дать вам фундаментальные знания и обеспечить быстрый доступ к нужной информации при использовании книги в качестве справочника.
Алфавит
Буквы
Изучая в школе родной язык, вы начинали с букв, слов и простейших правил синтаксиса. Для постижения основ языка Delphi мы предлагаем вам сделать то же самое.
Текст программы на языке Delphi формируется с помощью букв, цифр и специальных символов.
Буквы — это прописные и строчные символы латинского алфавита и символ подчеркивания:
Цифры представлены стандартной арабской формой записи:
применяются в основном в качестве знаков арифметических операций, разделителей, ограничителей и т.д. Из специальных символов формируются составные символы :
Они служат, в частности, для обозначения операций типа «не равно», «больше или равно», указания диапазонов значений, комментирования программы, т.д.
Все перечисленные знаки отражены на клавиатуре и при нажатии соответствующих клавиш появляются на экране. Как вы видите, среди них нет русских букв, хотя на клавиатуре вашего компьютера они наверняка присутствуют. Дело в том, что такие буквы в языке Delphi несут чисто информационную нагрузку и используются только в качестве данных или при написании комментария к программе.
Числа
Одно и то же число можно записать самыми разными способами, например:
В языке Delphi имеется возможность применять все способы записи, но чаще всего используют целые и вещественные числа.
Целые числа состоят только из цифр и знака + или -. Если знак опущен и число не равно 0, то оно рассматривается как положительное, например:
Вещественные числа содержат целую и дробную части, разделенные точкой:
Вещественные числа могут быть представлены в двух формах: с фиксированной и плавающей точкой.
Форма с фиксированной точкой совпадает с обычной записью чисел, например:
Форма с плавающей точкой используется при работе с очень большими или очень малыми числами. В этой форме число, стоящее перед буквой E, умножается на 10 в степени, указанной после буквы E:
Число, стоящее перед буквой E, называется мантиссой , а число после буквы E — порядком .
В этой статье мы чаще будем использовать форму с фиксированной точкой, так как она воспринимается лучше второй формы и совпадает с привычной математической записью чисел.
Слова-идентификаторы
Неделимые последовательности символов алфавита образуют слова ( идентификаторы ). Идентификатор начинается с буквы и не должен содержать пробелов. После первого символа допускаются буквы и цифры. Напоминаем, что символ подчеркивания считается буквой.
При написании идентификаторов могут использоваться как прописные, так и строчные буквы (между ними не делается различий). Длина идентификатора может быть любой, но значимы только первые 255 символов (вполне достаточный предел, не так ли). Примеры написания идентификаторов приведены ниже:
Правильно
Неправильно
Все идентификаторы подразделяются на зарезервированные слова, стандартные директивы, стандартные идентификаторы и идентификаторы программиста.
Зарезервированные (ключевые) слова составляют основу языка Delphi, любое их искажение вызовет ошибку компиляции. Вот полный перечень зарезервированных слов:
Стандартные директивы интерпретируются либо как зарезервированные слова, либо как идентификаторы программиста в зависимости от контекста, в котором используются. Вот они:
Стандартные идентификаторы — это имена стандартных подпрограмм, типов данных языка Delphi, т.д. В качестве примера приведем имена подпрограмм ввода и вывода данных и нескольких математических функций. Вы, без сомнения, сами угадаете их назначение:
Идентификаторы программиста определяются программистом, т.е вами, и носят произвольный характер. Если идентификатор состоит из двух или более смысловых частей, то для удобства их лучше выделять заглавной буквой или разделять символом подчеркивания:
Имя идентификатора обязательно должно нести смысловую нагрузку, тогда вы сможете читать программу как книгу и не потратите время на расшифровку непонятных обозначений.
Комментарии
С помощью комментариев вы можете пояснить логику работы своей программы. Комментарий пропускается компилятором и может находиться в любом месте программы. Комментарием является:
Данные
Понятие типа данных
Программа в процессе выполнения всегда обрабатывает какие-либо данные. Данные могут представлять собой целые и дробные числа, символы, строки, массивы, множества и др. Так как компьютер всего лишь машина, для которой данные — это последовательность нулей и единиц, он должен абсолютно точно «знать», как их интерпретировать. По этой причине все данные в языке Delphi подразделены на типы. Для описания каждого типа данных существует свой стандартный идентификатор: для целых — Integer, для дробных — Real, для строк — string и т.д. Программист может образовывать собственные типы данных и давать им произвольные имена (о том, как это делается, мы поговорим чуть позже).
Тип данных показывает, какие значения принимают данные и какие операции можно с ними выполнять. Каждому типу данных соответствует определенный объем памяти, который требуется для размещения данных. Например, в языке Delphi существует тип данных Byte. Данные этого типа принимают значения в целочисленном диапазоне от 0 до 255, могут участвовать в операциях сложения, вычитания, умножения, деления, и занимают 1 байт памяти.
Все типы данных в языке Delphi можно расклассифицировать следующим образом:
- простые типы данных. Они в свою очередь подразделяются на порядковые и вещественные типы данных. К порядковым типам относятся целочисленные, символьные, булевские, перечисляемые и интервальные типы данных;
- временной тип данных. Служит для представления значений даты и времени;
- строковые типы данных. Служат для представления последовательностей из символов, например текста;
- составные типы данных (в некоторых источниках — структурированные типы данных). Формируются на основе всех остальных типов. К ним относятся массивы, множества, записи, файлы, классы и ссылки на классы;
- процедурные типы данных. Позволяют манипулировать процедурами и функциями как данными программы;
- указательные типы данных. Данные этих типов хранят адреса других данных, с их помощью организуются различные динамические структуры: списки, деревья и т.д.;
- тип данных с непостоянным типом значений. Служит для представления значений, тип которых заранее неизвестен; с его помощью легко организуется работа со списком разнотипных значений;
Некоторые предопределенные типы данных делятся на фундаментальные и обобщенные типы. Данные фундаментальных типов имеют неизменный диапазон значений и объем занимаемой памяти на всех моделях компьютеров. Данные обобщенных типов на различных моделях компьютеров могут иметь разный диапазон значений и занимать разный объем памяти. Деление на фундаментальные и обобщенные типы характерно для целых, символьных и строковых типов данных.
По ходу изложения материала мы рассмотрим все перечисленные типы данных и более подробно объясним их смысл и назначение в программе.
Константы
Данные, независимо от типа, имеют некоторое значение и в программе предстают как константы или переменные. Данные, которые получили значение в начале программы и по своей природе изменяться не могут, называются константами . Константами, например, являются скорость света в вакууме и соотношение единиц измерения (метр, сантиметр, ярд, фут, дюйм), которые имеют научно обоснованные или традиционно принятые постоянные значения. Константы описываются с помощью зарезервированного слова const . За ним идет список имен констант, каждому из которых с помощью знака равенства присваивается значение. Одно присваивание отделяется от другого с помощью точки с запятой. Тип константы распознается компилятором автоматически, поэтому его не надо указывать при описании. Примеры констант:
После такого описания для обращения к нужному значению достаточно указать лишь имя соответствующей константы.
Значение константы можно задавать и выражением. Эту возможность удобно использовать для комплексного представления какого-либо понятия. Например, временной промежуток, равный одному месяцу, можно задать так:
Очевидно, что, изменив базовую константу SecondsInMinute, можно изменить значение константы SecondsInDay.
При объявлении константы можно указать ее тип:
Такие константы называются типизированными; их основное назначение — объявление константных значений составных типов данных.
Переменные
Переменные в отличие от констант могут неограниченное число раз менять свое значение в процессе работы программы. Если в начале программы некоторая переменная X имела значение 0, то в конце программы X может принять значение 10000. Так бывает, например, при суммировании введенных с клавиатуры чисел.
Переменные описываются с помощью зарезервированного слова var . За ним перечисляются идентификаторы переменных, и через двоеточие указывается их тип. Каждая группа переменных отделяется от другой группы точкой с запятой. Например:
В теле программы переменной можно присвоить значение. Для этого используется составной символ :=, например:
Вы можете присвоить значение переменной непосредственно при объявлении:
Объявленные таким образом переменные называются инициализированными . На инициализированные переменные накладывается ограничение: они не могут объявляться в подпрограммах (процедурах и функциях). Если переменная не инициализируется при объявлении, то по умолчанию она заполняется нулем.
Каждый используемый в программе элемент данных должен быть описан в разделе const или var . Исключение составляют данные, заданные непосредственно значением , например:
Простые типы данных
Целочисленные типы данных
Целочисленные типы данных применяются для описания целочисленных данных. Для решения различных задач могут потребоваться различные целые числа. В одних задачах счет идет на десятки, в других — на миллионы. Соответственно в языке Delphi имеется несколько целочисленных типов данных, среди которых вы можете выбрать наиболее подходящий для своей задачи.
Расставляем точки над i в Delphi RAII
Тема RAII в Delphi обычно замалчивается или же информация по этому вопросу ограничивается обсуждением полезности интерфейсов. Но интерфейсы поодиночке не дают многих желаемых возможностей. Когда в Delphi 2006 появилась перегрузка операций, приватные поля записей, собственные конструкторы и методы в записях и, казалось, было бы логично увидеть и автоматически вызываемый деструктор. И run-time позволяет, и в разделе запроса новых фич Delphi на протяжении нескольких лет в ТОП–10 висит запрос №21729 «Record Operator Overloading: Please implement «Initialize» and «Finalize» operators». Наверное, не судьба. Ничего, я покажу, как обойтись без несостоявшихся фич. Так как Delphi 7 живее всех живых, будут рассмотрены решения, совместимые с Delphi 7 в том числе
Времени найти обходные пути было достаточно. Эта статья — не tutorial и рассчитана на продвинутых разработчиков на Delphi, пишущих собственные библиотеки или делающих привязки к библиотекам на других языках программирования
Зачем хочется RAII в Delphi?
- Автоматическое управление памятью. Лестница из try… finally — это не серьёзно. TList, TComponentList и прочие требуют дисциплины от того, кто будет их применять. Особенно сложно, не используя автоматику, сделать корректное освобождение памяти для переменных, используемых из восходящего замыкания
- Copy-on-write и счётчики ссылок
- Другое особое поведение при копировании
- Copy-on-write и счётчики ссылок для объектов, созданных в сторонних библиотеках (например, CFString)
Для каких типов Delphi действует автоматическое управление?
- AnsiString, WideString, UnicodeString — строки
- array of… — динамические массивы
- reference to… — замыкания в Delphi 2009+
- интерфейсы
- Variant, OleVariant — варианты
Среди всех этих возможностей программируемыми являются только интерфейсы и варианты
Чем плохи интерфейсы?
- Инициализируются nil, у которого нельзя вызывать методы.
Не подходит, если вы хотите реализовать собственный тип строки или собственную длинную арифметику. Неинициализированная переменная должна вести себя как пустая строка или 0, соответственно - Методы не могут изменить содержимое переменной–указателя, у которого они были вызваны
- Нет контроля за тем, что происходит при копировании объекта. Только AddRef, который не может изменить содержимое переменной–указателя
- Нет встроенной возможности сделать copy-on-write
- Нет перегрузки операций
Чем плохи варианты?
- Инициализируются Unassigned, у которого также нельзя вызвать методы
- Вызовы нетипизированы. Реализация IDispatch или диспетчеризации у вариантов — нетривиальная и слабо документированная область знаний
- Необходимость реализации муторных конверсий между другими типами вариантов, всяческих вспомогательных методов, которые могут быть вызваны
Как решить большинство этих проблем?
Решение, которое я предлагаю — заворачивать интерфейсы или варианты внутрь приватной части записей (record). Объявляем тип записи. Объявляем тип интерфейса. Дублируем все методы и в интерфейсе, и в записи. Методы записи перенаправляют все вызовы внутреннему объекту, при этом можно сделать то, что сама по себе переменная интерфейсного типа сделать не способна
В реализации каждого метода записи предусматриваем случай, когда в приватном поле nil — может потребоваться автоматически инициализировать объект перед тем, как что–либо вызывать у него. Если нужно реализовать Copy-on-write, в интерфейсе объявляется метод
Этот метод определяет по счётчику ссылок свою уникальность. Если объект не уникален, объект должен создать свою копию и записать этот указатель в Obj вместо себя. Каждый метод записи, который может что–либо изменить, перед передачей управления методу интерфейса должен убедиться в уникальности указателя. Для внутренних нужд можно и у других методов интерфейса предусмотреть var Obj: IOurInterface. Например, по аналогии со встроенными строками может возникнуть желание сделать так, чтобы, когда в строке собственного типа не остаётся символов, динамически размещённый объект удалялся, а внутренний указатель становился nil
В целях оптимизации при реализации собственных строк или длинной арифметики может потребоваться предусмотреть специальный случай a := a + b. Не гарантирую, что это сработает, но можно попробовать при реализации операции + сравнивать указатели @ Self и @ Result
Принципиально неразрешима проблема безусловной автоматической инициализации внутреннего поля, но можно инициализировать при первом обращении. Остальные проблемы разрешимы либо завёртыванием интерфейса в запись, либо завёртыванием варианта в запись, об этом далее
Вариант в записи — это как зефир в глазури, но вариант в записи
Собственный тип варианта даёт более полное управление по сравнению с интерфейсом. Так как вариантное поле приватно и наружу этот вариант не должен утекать, можно реализовать лишь минимальный набор методов собственного (custom) типа варианта. Если не считать отладчика, пытающегося привести (CastTo) вариант к строке при наведении курсора, потребуется реализовать копирование (Copy) и уничтожение (Clear) варианта. В оперативной памяти собственные типы варианта, как правило, состоят из маркера типа варианта и указателя (например, наследник TObject). Как это делается, предлагаю посмотреть на примере реализации комплексных чисел (VarCmplx.pas), который присутствует, по крайней мере, начиная с Delphi 7
Использование вариантов пригодилось бы для однозвенной обёртки CFString. Если делать обёртку для интерфейсов, Delphi будет вызывать AddRef и Release у интерфейса, но CFString — не интерфейс, и потребуется либо обернуть CFString в дополнительный слой косвенности из интерфейса, либо использовать собственный тип варианта, который вызывает CFRetain и CFRelease, требуемые для нормального управления памятью CFString. Это работало бы гораздо лучше, чем та обёртка CFString, которую предлагает Embarcadero в Delphi XE2
Эй, а как же Delphi 7?
Delphi — язык с длинной историей, и до того, как появилась объектная система Delphi, в Borland Pascal with Objects была другая объектная система. В Delphi 7 и Delphi 2005 она по–прежнему функционирует. Вместо record пишется ключевое слово object, и получившийся тип во многом похож на record в Delphi 2006: у него могут быть приватные поля, у него могут быть методы. object’ы одного типа можно присваивать друг другу, в этом смысле они тоже аналогичны record. Как раз то, что нам нужно. Компилятор будет ругаться на небезопасный тип, нет перегрузки операций, но это единственные неудобства. Сходство object и record настолько велико, что можно, используя условные директивы компилятора, на старых версиях Delphi объявлять тип как object, а на новых — как record. Именно так я поступил в своей небольшой библиотеке коллекций Delphi-CVariants
Проблемы могут возникнуть, если пытаться объявить несколько таких типов, использующих друг друга. Цикличные зависимости в исходном коде предусмотрены для классов, интерфейсов и указателей, но не для object’ов as is. Предпочтительнее объявлять object’ы так, чтобы каждый следующий знал про предыдущие, но не наоборот. Поэтому, например, в моей библиотечке CMapIterator знает про CVariant, но CVariant не знает про CMapIterator
Работа с API онлайн-сервисов в Delphi. Авторизация и работа с методами API
Это вторая часть большой статьи про использование API онлайн-сервисов в Delphi. В первой части мы определились с тем, что необходимо знать прежде, чем начинать свои разработки.
В этой части мы рассмотрим весь процесс написания своего первого модуля Delphi для работы с API онлайн-сервиса.
Так как эта часть посвящено работе в Delphi, то здесь будет достаточно много кода.
Содержание части
Авторизация и получение доступа к API
Один из ключевых моментов реализации любого API — авторизация пользователя. Как уже было сказано ранее, в настоящее время наиболее активно используется авторизация пользователей по протоколу OAuth, а точнее — OAuth 2.0. Чтобы авторизовать пользователя по OAuth вы можете написать свой собственный класс или же, если Вы используете Delphi XE5-XE7, то можете воспользоваться компонентами REST Client Library. Чтобы не повторяться о том, как это сделать, я просто приведу здесь ссылки на статьи где рассматривалась авторизация по OAuth в разных онлайн-сервисах:
- Серия статей про OAuth в Google:
- Google API в Delphi. OAuth для Delphi-приложений,
- Google API в Delphi. Обновление модуля для OAuth,
- Решение проблем с Google OAuth 2.0. для Win-приложений,
- Тестирование запросов к API Google средствами Delphi. Компонент OAuthClient для Delphi XE — XE3.
- Использование REST Client Library для OAuth:
- Delphi XE5: REST Client Library,
- Delphi: авторизация по OAuth 2.0 в Dropbox своими силами,
- REST Client Library: использование API ВКонтакте
Однако встречаются и такие API в которых авторизация пользователя проводится по собственным протоколам и правилам. В этом случае вам необходимо самостоятельно реализовать процедуру авторизации (для чего необходимо знать всё то, о чем сказано в первой части статьи). Рассмотрим пример работы с подобными API.
Пример с etxt.ru
Начинаем читать документацию. Что нам говорит сервис:
Порядок следования параметров в запросе значения не имеет, порядок параметров важен только при расчете подписи.
API-ключ token уникален для каждого пользователя и его можно узнать в разделе «Мой профиль/Настройка интерфейса».
Подпись sign расcчитывается по алгоритмe, приведенному ниже. Подписываются только параметры, переданные по GET.
Важные моменты в этой части документации выделены жирным:
- Все текстовые параметры запроса передаются в кодировке UTF-8
- Для каждого запроса нам необходимо рассчитывать по специальному алгоритму подпись.
- Порядок следования параметров в самом запросе не важен
- При расчёте подписи все параметры должны следовать в строго определенном порядке.
Что это всё значит? Ну, с кодировкой, допустим, всё понятно. В остальном же получается, что доступ к API предоставляется нам только, если мы передадим на сервер два верных параметра — это token (он не меняется и получается при регистрации пользователя) и sign — подпись, которая меняется при каждом запросе. Следовательно, нам необходимо в своей программе предусмотреть специальный метод, который будет рассчитывать нам эту подпись. Попробуем написать такой метод.
Снова смотрим документацию. Вот, что говорит нам сервис про алгоритм подписи запроса:
Значение params — это конкатенация пар «имя=значение» отсортированных в алфавитом порядке по «имя», где «имя» — это название параметра, передаваемого в функцию API, «значение» — значение параметра. Разделитель в конкатенации не используется. Параметр sign при расчете подписи не учитывается, все остальные параметры запроса должны учитываться при расчете.
Теперь попробуем составить алгоритм расчёта такой подписи. Итак, что нам нужно:
- Найти в ключ api_pass (он как и token нам выдается сервисом один раз и на всю жизнь)
- Необходимо отсортировать все параметры запроса в алфавитном порядке
- Необходимо произвести конкатенацию, т.е. «склеивание» параметров
- Рассчитать MD5 для строки api_pass.’api-pass’
- Полученную в п.3 строку «склеить» с результатов п.4
- Рассчитать для полученной в п.5 строки MD5- это и будет наша подпись.
Реализуем этот алгоритм в Delphi.
Так как в дальнейшем предстоит использовать этот API, то я создал отдельный класс, который постепенно будет «обрастать» новыми методами и свойствами для работы с API. Класс этот вынесен в отдельный модуль и на данном этапе выглядит так:
Свойства Token и ApiPass — это ключ доступа и пароль к API, которые, как мы уже выяснили никогда не меняются. Теперь рассмотрим метод SignRequest, который будет вычислять подпись и добавлять её к параметрам запроса:
Так как параметр sign не участвует в расчёте подписи, то вначале мы проверили есть ли такой параметр в списке и, если sign присутствует, то удалили его. Далее мы проверили наличие параметра token и, при необходимости, добавили его в список. После этого мы отсортировали весь список параметров в алфавитном порядке и составили строку params. Для расчёта MD5 мы воспользовались возможностями класса TIdHashMessageDigest, который находится в модуле IdHashMessageDigest. Расчёт производился в два шага:
- Рассчитали хэш пароля API.
- Полученный хэш добавили к строке params и рассчитали новый хэш для полученной строки
После этого добавили подпись в параметр sign запроса. Теперь список AParams содержит все необходимые параметры для выполнения запроса к API. Как проверить, что рассчитанная подпись верная? Очень просто — попробовать выполнить какой-нибудь простой запрос к API.
Выполнение запросов к API
Выполнив авторизацию и получив доступ к API мы можем выполнять различные запросы к API. Различные API требуют могут предъявлять разные требования к выполнению запросов. И прежде, чем начинать писать код в Delphi, опять же, следует внимательно прочитать документацию к API и определиться с тем как могут выглядеть различные запросы к одному и тому же API.
В URL запроса всегда присутствует общая для всех запросов часть. Так, например, если используется API, использующий REST-принципы (любой API Яндекса, Google, ВКонтакте и т.д.), то запросы к такому серверу могут иметь следующий вид:
- http://example.com/api/book/1
- http://example.com/api/lists/
- http://example.com/api/authors/123
- и т.д.
Видите? В каждом из запросов есть http://example.com/api/. В различной документации к API этот URL может называться по-разному: точка доступа, Base URL или просто URL запроса. Base URL всегда следует выносить в раздел констант. Объясню почему это стоит делать. Причин две:
- Для того, чтобы не использовать в дальнейшем в своем коде одну и ту же строку по 100 раз и избегать случайных опечаток, которые потом довольно сложно обнаружить в большом массиве кода
- Редко, но тем не менее встречается ситуация, когда сервер изменяет Base URL полностью или частично. Если произойдет смена Base URL, например, в новой версии API, то вам будет достаточно изменить всего одну константу в коде.
Определившись с Base URL можно начать реализовывать выполнение запросов к API в Delphi. Рассмотрим это, опять же, на примере API etxt.ru.
Пример выполнения запросов к etxt.ru
Определяем Base URL. В случае с etxt.ru этот URL указан в документации и выглядит так:
Этот URL не изменяется — изменяются только параметры запроса. Так, например, запрос к списку категорий может выглядеть так:
https://www.etxt.ru/api/json/?token=12345&method=categories.listCategories&sign=1234fde4567ef
при запросе списка папок запрос будет таким:
https://www.etxt.ru/api/json/?token=12345&method=folders.listFolders&sign=1dnt34dde4567ee
То есть, наша константа в Delphi может выглядеть так:
Так как сервер требует доступа по https, то для дальнейшей работы нам потребуются два компонента Indy: TidHTTP и TIdSSLIOHandlerSocketOpenSSL, которые находятся, соответственно, в модулях idHTTP и idSSLOpenSSL. Так же нам потребуются две динамические библиотеки: libeay32.dll и ssleay32.dll, которые вы можете скачать со страницы с исходниками.
Добавим TidHTTP и TIdSSLIOHandlerSocketOpenSSL в наш класс для работы с API:
Динамические библиотеки необходимо положить в папку с exe-файлом приложения. Теперь напишем новый метод нашего класса, который будет выполнять GET-запрос на сервер и возвращать ответ. С учётом того, что у нас уже написана процедура подписи запроса, наш новый метод может выглядеть так:
Метод получает на входе список параметров, затем подписывает запрос, отправляет его на сервер и записывает полученный ответ в Result. Проверим работу нашего метода.
Для этого создадим новое приложение VCL, подключим в uses модуль с нашим классом, а на главную форму бросим всего два компонента — TButton и TMemo:
В обработчике OnClick кнопки напишем следующий код:
Если наша подпись была рассчитана верно, то в результате мы должны получить в Memo JSON-объект с данными по категориям. Запускаем приложение, кликаем по кнопке и видим следующий результат:
Результат получен, следовательно, можно приступать к следующему шагу работы над API — разбору результатов запроса.
Парсинг результатов запроса
На предыдущем шаге работы с API мы получили от сервера «сырые» для будущего приложения данные. То есть на данный момент ни наше приложение ни наш класс для работы с API «не знают» что делать с данными — это простая строка, которую необходимо правильно разобрать и представить пользователю приложения.
В своей работе с самыми различными API я придерживаюсь следующих двух положений:
-
Один класс используется непосредственно для обмена данными с сервером: в этом классе реализованы методы выполнения GET-, POST-, DELETE- и других запросов к API по HTTP
Мне такая схема работы представляется наиболее удобной в плане отладки. Вы же в своих приложениях вольны делать как угодно.
Для разбора ответов сервера, как я упоминал в первой части статьи, Вы должны понимать, хотя бы, что такое XML и JSON и как их можно разобрать в Delphi. Данные, полученные от сервера, внутри своей программы вы можете хранить и представлять как вам угодно — хранить в виде простой строки, создавать свои собственные классы, записи (record) и т.д. Внутри вашего приложения — вы хозяин и только Вы решаете как хранить и использовать полученные данные.
Опять же (и я не устану это повторять), прежде чем писать код необходимо прочитать документацию по API. На этот раз надо изучить то:
- какие свойства содержат возвращаемые объекты и типы данных этих свойств
- какие общие свойства есть у всех объектов API (и есть ли такие общие свойства, в принципе).
Если упустить этот момент, то в итоге вы можете сильно «раздуть» свой код повторяющимися свойствами родственных объектов. Например, в API Box.com можно встретить объекты Folder (папка) и MiniFolder (краткая информация о той же папке). В этом случае лучше всего в Delphi сделать класс TFolder дочерним от TMinifolder — упростит, в дальнейшем, отладку приложения, сократит код и, плюс, поможет избежать ошибок при парсинге.
Чем просматривать ответы сервера? Если данные приходят в JSON, то могу вам порекомендовать использовать онлайн-сервис http://jsonviewer.stack.hu/. Вот как выглядит в этом сервисе объект, полученный в предыдущем примере:
Как видно на рисунке, все поля объектов представляют из себя обычные строки. Разобрать такой объект будет достаточно просто.
Для примера, рассмотрим как можно разбирать, хранить и представлять данные от сервера etxt.ru.
Разбор данных etxt.ru
Итак, класс для работы с сервером по HTTP у нас есть (впоследствии мы можем добавить в него, например, метод выполнения POST-запроса к серверу или любой другой по необходимости) — его содержимого нам пока хватит для реализации разных методов API.
В предыдущем примере мы получили большой JSON-объект с тематическими категориями. Посмотрим из чего состоит объект категории. Читаем документацию:
categories.listCategories
Возвращает список тематических категорий заказов/статей, отсортированный по названию категории.
Результат
Поле | Описание |
---|---|
id_category | Идентификатор категории |
id_parent | Идентификатор родительской категории |
name | Название категории |
keyword | Ключевое слово категории |
На языке Delphi это может быть, например, такой класс:
Программирование на языке Delphi
Глава 2. Основы языка Delphi
Авторы: А.Н. Вальвачев
К.А. Сурков
Д.А. Сурков
Ю.М. Четырько
Опубликовано: 12.11.2005
Исправлено: 10.12.2020
Версия текста: 1.0
В основе среды Delphi лежит одноименный язык программирования — Delphi, ранее известный как Object Pascal. При разработке программы среда Delphi выполняет свою часть работы — создает пользовательский интерфейс согласно вашему дизайну, а вы выполняете свою часть — пишите обработчики событий на языке Delphi. Объем вашей работы зависит от программы: чем сложнее алгоритм, тем тяжелее ваш труд. Необходимо заранее усвоить, что невозможно заставить средство разработки делать всю работу за вас. Некоторые задачи среда Delphi действительно полностью берет на себя, например создание простейшей программы для просмотра базы данных. Однако большинство задач не вписываются в стандартные схемы — вам могут понадобиться специализированные компоненты, которых нет в палитре компонентов, или для задачи может не оказаться готового решения, и вы вынуждены будете решать ее старым дедовским способом — с помощью операторов языка Delphi. Поэтому мы настоятельно рекомендуем вам не игнорировать эту главу, поскольку на практике вы не избежите программирования. Мы решили изложить язык в одной главе, не размазывая его по всей книге, чтобы дать вам фундаментальные знания и обеспечить быстрый доступ к нужной информации при использовании книги в качестве справочника.
2.1. Алфавит
2.1.1. Буквы
Изучая в школе родной язык, вы начинали с букв, слов и простейших правил синтаксиса. Для постижения основ языка Delphi мы предлагаем вам сделать то же самое.
Текст программы на языке Delphi формируется с помощью букв, цифр и специальных символов.
Буквы — это прописные и строчные символы латинского алфавита и символ подчеркивания:
Цифры представлены стандартной арабской формой записи:
применяются в основном в качестве знаков арифметических операций, разделителей, ограничителей и т.д. Из специальных символов формируются составные символы :
Они служат, в частности, для обозначения операций типа «не равно», «больше или равно», указания диапазонов значений, комментирования программы, т.д.
Все перечисленные знаки отражены на клавиатуре и при нажатии соответствующих клавиш появляются на экране. Как вы видите, среди них нет русских букв, хотя на клавиатуре вашего компьютера они наверняка присутствуют. Дело в том, что такие буквы в языке Delphi несут чисто информационную нагрузку и используются только в качестве данных или при написании комментария к программе.
2.1.2. Числа
Одно и то же число можно записать самыми разными способами, например:
В языке Delphi имеется возможность применять все способы записи, но чаще всего используют целые и вещественные числа.
Целые числа состоят только из цифр и знака + или – . Если знак опущен и число не равно 0, то оно рассматривается как положительное, например:
Вещественные числа содержат целую и дробную части, разделенные точкой:
Вещественные числа могут быть представлены в двух формах: с фиксированной и плавающей точкой.
Форма с фиксированной точкой совпадает с обычной записью чисел, например:
Форма с плавающей точкой используется при работе с очень большими или очень малыми числами. В этой форме число, стоящее перед буквой E, умножается на 10 в степени, указанной после буквы E:
Число, стоящее перед буквой E, называется мантиссой , а число после буквы E — порядком .
В этой книге мы чаще будем использовать форму с фиксированной точкой, так как она воспринимается лучше второй формы и совпадает с привычной математической записью чисел.
2.1.3. Слова-идентификаторы
Неделимые последовательности символов алфавита образуют слова ( идентификаторы ). Идентификатор начинается с буквы и не должен содержать пробелов. После первого символа допускаются буквы и цифры. Напоминаем, что символ подчеркивания считается буквой.
При написании идентификаторов могут использоваться как прописные, так и строчные буквы (между ними не делается различий). Длина идентификатора может быть любой, но значимы только первые 255 символов (вполне достаточный предел, не так ли). Примеры написания идентификаторов приведены ниже:
Правильно | Неправильно |
---|---|
RightName | Wrong Name |
E_mail | E–mail |
_5inches | 5inches |
Все идентификаторы подразделяются на зарезервированные слова, стандартные директивы, стандартные идентификаторы и идентификаторы программиста.
Зарезервированные (ключевые) слова составляют основу языка Delphi, любое их искажение вызовет ошибку компиляции. Вот полный перечень зарезервированных слов:
Стандартные директивы интерпретируются либо как зарезервированные слова, либо как идентификаторы программиста в зависимости от контекста, в котором используются. Вот они:
Стандартные идентификаторы — это имена стандартных подпрограмм, типов данных языка Delphi, т.д. В качестве примера приведем имена подпрограмм ввода и вывода данных и нескольких математических функций. Вы, без сомнения, сами угадаете их назначение:
Идентификаторы программиста определяются программистом, т.е вами, и носят произвольный характер. Если идентификатор состоит из двух или более смысловых частей, то для удобства их лучше выделять заглавной буквой или разделять символом подчеркивания:
Имя идентификатора обязательно должно нести смысловую нагрузку, тогда вы сможете читать программу как книгу и не потратите время на расшифровку непонятных обозначений.
2.1.4. Комментарии
С помощью комментариев вы можете пояснить логику работы своей программы. Комментарий пропускается компилятором и может находиться в любом месте программы. Комментарием является:
2.2. Данные
2.2.1. Понятие типа данных
Программа в процессе выполнения всегда обрабатывает какие-либо данные. Данные могут представлять собой целые и дробные числа, символы, строки, массивы, множества и др. Так как компьютер всего лишь машина, для которой данные — это последовательность нулей и единиц, он должен абсолютно точно «знать», как их интерпретировать. По этой причине все данные в языке Delphi подразделены на типы. Для описания каждого типа данных существует свой стандартный идентификатор: для целых — Integer, для дробных — Real, для строк — string и т.д. Программист может образовывать собственные типы данных и давать им произвольные имена (о том, как это делается, мы поговорим чуть позже).
Тип данных показывает, какие значения принимают данные и какие операции можно с ними выполнять. Каждому типу данных соответствует определенный объем памяти, который требуется для размещения данных. Например, в языке Delphi существует тип данных Byte. Данные этого типа принимают значения в целочисленном диапазоне от 0 до 255, могут участвовать в операциях сложения, вычитания, умножения, деления, и занимают 1 байт памяти.
Все типы данных в языке Delphi можно расклассифицировать следующим образом:
- простые типы данных. Они в свою очередь подразделяются на порядковые и вещественные типы данных. К порядковым типам относятся целочисленные, символьные, булевские, перечисляемые и интервальные типы данных;
- временной тип данных. Служит для представления значений даты и времени;
- строковые типы данных. Служат для представления последовательностей из символов, например текста;
- составные типы данных (в некоторых источниках — структурированные типы данных). Формируются на основе всех остальных типов. К ним относятся массивы, множества, записи, файлы, классы и ссылки на классы;
- процедурные типы данных. Позволяют манипулировать процедурами и функциями как данными программы;
- указательные типы данных. Данные этих типов хранят адреса других данных, с их помощью организуются различные динамические структуры: списки, деревья и т.д.;
- тип данных с непостоянным типом значений. Служит для представления значений, тип которых заранее неизвестен; с его помощью легко организуется работа со списком разнотипных значений;
Некоторые предопределенные типы данных делятся на фундаментальные и обобщенные типы. Данные фундаментальных типов имеют неизменный диапазон значений и объем занимаемой памяти на всех моделях компьютеров. Данные обобщенных типов на различных моделях компьютеров могут иметь разный диапазон значений и занимать разный объем памяти. Деление на фундаментальные и обобщенные типы характерно для целых, символьных и строковых типов данных.
По ходу изложения материала мы рассмотрим все перечисленные типы данных и более подробно объясним их смысл и назначение в программе.
2.2.2. Константы
Данные, независимо от типа, имеют некоторое значение и в программе предстают как константы или переменные. Данные, которые получили значение в начале программы и по своей природе изменяться не могут, называются константами . Константами, например, являются скорость света в вакууме и соотношение единиц измерения (метр, сантиметр, ярд, фут, дюйм), которые имеют научно обоснованные или традиционно принятые постоянные значения. Константы описываются с помощью зарезервированного слова const . За ним идет список имен констант, каждому из которых с помощью знака равенства присваивается значение. Одно присваивание отделяется от другого с помощью точки с запятой. Тип константы распознается компилятором автоматически, поэтому его не надо указывать при описании. Примеры констант:
После такого описания для обращения к нужному значению достаточно указать лишь имя соответствующей константы.
Значение константы можно задавать и выражением. Эту возможность удобно использовать для комплексного представления какого-либо понятия. Например, временной промежуток, равный одному месяцу, можно задать так:
Очевидно, что, изменив базовую константу SecondsInMinute, можно изменить значение константы SecondsInDay.
При объявлении константы можно указать ее тип:
Такие константы называются типизированными; их основное назначение — объявление константных значений составных типов данных.
2.2.3. Переменные
Переменные в отличие от констант могут неограниченное число раз менять свое значение в процессе работы программы. Если в начале программы некоторая переменная X имела значение 0, то в конце программы X может принять значение 10000. Так бывает, например, при суммировании введенных с клавиатуры чисел.
Переменные описываются с помощью зарезервированного слова var . За ним перечисляются идентификаторы переменных, и через двоеточие указывается их тип. Каждая группа переменных отделяется от другой группы точкой с запятой. Например:
В теле программы переменной можно присвоить значение. Для этого используется составной символ := , например:
Вы можете присвоить значение переменной непосредственно при объявлении:
Объявленные таким образом переменные называются инициализированными . На инициализированные переменные накладывается ограничение: они не могут объявляться в подпрограммах (процедурах и функциях). Если переменная не инициализируется при объявлении, то по умолчанию она заполняется нулем.
Каждый используемый в программе элемент данных должен быть описан в разделе const или var . Исключение составляют данные, заданные непосредственно значением , например:
2.3. Простые типы данных
2.3.1. Целочисленные типы данных
Целочисленные типы данных применяются для описания целочисленных данных. Для решения различных задач могут потребоваться различные целые числа. В одних задачах счет идет на десятки, в других — на миллионы. Соответственно в языке Delphi имеется несколько целочисленных типов данных, среди которых вы можете выбрать наиболее подходящий для своей задачи (таблица 2.1).
Фундаментальные типы данных:
Тип данных | Диапазон значений | Объем памяти (байт) |
---|---|---|
Byte | 0..255 | 1 |
Word | 0..65535 | 2 |
Shortint | –128..127 | 1 |
Smallint | –32768..32767 | 2 |
Longint | –2147483648..2147483647 | 4 |
Longword | 0.. 4294967295 | 4 |
Int64 | –2^63..2^63–1 | 8 |
Обобщенные типы данных:
Тип данных | Диапазон значений | Формат (байт) |
---|---|---|
Cardinal | 0.. 4294967295 | 4* |
Integer | –2147483648..2147483647 | 4* |
ПРИМЕЧАНИЕ
* — количество байт памяти, требуемых для хранения переменных обобщенных типов данных, приведено для 32-разрядных процессоров семейства x86. Пример описания целочисленных данных: Позволим себе дать небольшой совет. При программировании алгоритмов предпочтение следует отдавать обобщенным типам даных, поскольку они позволяют достичь максимальной производительности программ при переходе на другие модели компьютеров (например, при переходе на компьютеры, построенные на основе новых 64-разрядных процессоров). Переменные обобщенных типов данных могут храниться в памяти по-разному в зависимости от конкретной модели компьютера, и для работы с ними компилятор может генерировать наиболее оптимальный код. Однако при использовании переменных обобщенных типов данных ни в коем случае нельзя полагаться на формат их хранения в памяти, в частности на размер. 2.3.2. Вещественные типы данныхВещественные типы данных применяются для описания вещественных данных с плавающей или с фиксированной точкой (таблица 2.2).
|