Меню
Главная
Авторизация/Регистрация
 
Главная arrow Информатика arrow Алгоритмы и структуры данных

ОСОБЕННОСТИ ЯЗЫКА ПРОГРАММИРОВАНИЯ DELPHI

В среде Delphi для разработки приложений используется язык программирования Delphi, основу которого составляет язык Object Pascal (объектно-ориентированное расширение стандартного языка Pascal) [19].

Все основные конструкции Borland Pascal 7.0 сохранены в языке Delphi. Коренному преобразованию подверглась модель объектов. Delphi поддерживает сразу две модели: «старую», появившуюся в Turbo Pascal 5.5, и «новую», ориентированную на среду визуального программирования. Сочетание старой и новой моделей в одной программе фирмой Borland декларируется как «возможное», однако без необходимости делать это не рекомендуется.

КОММЕНТАРИИ

Текст многострочного комментария ограничивается символами (* и *) или их эквивалентами { и }.

Однострочный комментарий содержит двойной слэш (//) в начале комментария.

ОПИСАНИЕ ПРОСТЫХ ТИПОВ ДАННЫХ

Некоторые простые типы данных (целочисленные, вещественные и символьные) делятся на базовые типы Delphi и машинно-ориентированные типы.

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

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

Базовые целочисленные типы Delphi приведены в табл. П2.1.

Базовые целочисленные типы Delphi

Имя типа

Диапазон

Представление в памяти

Shortint

-128... 127

1 байт, со знаком

Smallint

-32 768 ... 32 767

2 байта, со знаком

Longint

-2 147 483 648...2 147 483 647

4 байта, со знаком

Int64

—263... 263—1

8 байтов, со знаком

Byte

0... 255

1 байт, без знака

Word

0... 65 535

2 байта, без знака

LongWord

0... 4 294 967 295

4 байта, без знака

Машинно-ориентированные целые типы в Delphi представлены типами Integer и Cardinal. Тип Integer эквивалентен базовому типу Longint, а тип Cardinal — типу LongWord. Фактически они представляют собой знаковое и беззнаковое четырехбайтные числа.

Базовые вещественные типы Delphi приведены в табл. П2.2. Для типа Currency max = 922337203685477.5808.

Таблица П2.2

Базовые вещественные типы Delphi

Имя

типа

Минимальное

значение

Максимальное

значение

Точность (число цифр мантиссы)

Память,

байт

Real48

2,9 x К)“39

1,7 x Ю38

11-12

6

Single

1,7 x Kb45

3,4 x 1038

7-8

4

Double

5,0 x 10-324

1,7 x 10308

15-16

8

Extended

3,6 x io-4951

1,1 x 104932

19-20

10

Comp

-2 x 1063+1

2 x 1063- 1

19-20

8

Currency

-max

max

19-20

8

Машинно-ориентированный вещественный тип представлен типом Real, который эквивалентен базовому типу Double.

Значениями символьного типа являются отдельные символы. Базовые символьные типы представлены типами AnsiChar и WideChar.

Символ типа AnsiChar занимает один байт, а для кодирования символов используется код ANSI Американского национального института стандартов (American National Standards Institute). Символ типа WideChar занимает два байта, а для кодирования символов используется международный набор символов Unicode. Набор символов Unicode включает в свой состав более 60 тысяч элементов. Первые 256 символов Unicode совпадают с кодом ANSI.

Кроме базовых символьных типов в языке Delphi определен машинно-ориентированный тип Char, который эквивалентен типу Ansi Char.

ОСОБЕННОСТИ ОПИСАНИЯ И ИСПОЛЬЗОВАНИЯ

ФУНКЦИЙ

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

Имя функции нельзя использовать как простую переменную. Так, если имя функции внутри ее описания используется не в левой части оператора присваивания, то это означает, что функция вызывает себя рекурсивно.

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

При выходе из функции значение переменной Result автоматически присваивается имени функции.

Пример. Функция вычисления факториала числа п.

function Factorial(n: Byte): Longint;

var i: Byte;

begin

Result := 1; //0! = 1!= 1 по определению.

for i := 2 to n do

Result:= Result * i;

end;

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

СТРОКИ В DELPHI

Для обработки текстов в Delphi используются следующие типы:

  • 1) короткие строки ShortString и string[N], N<255;
  • 2) длинная строка string;
  • 3) нуль-терминальная строка PChar;
  • 4) широкая строка WideString.

Общим для этих типов является то, что каждая строка трактуется как одномерный массив символов, количество символов в котором может меняться в работающей программе: для string[N] длина строки меняется от 0 до N, для string и PChar — от 0 до 2 Гбайт.

Для коротких строк память выделяется статически (на этапе компиляции) в размере 256 байтов для ShortString и N + 1 байт для string[N]. Начальный байт (с номером 0) такой строки содержит текущую длину строки, а сами символы располагаются, начиная со следующего байта с номером 1. Поскольку для длины строки отводится один байт, максимальная длина короткой строки не может превышать 255 символов. Для объявления короткой строки максимальной длины может использоваться стандартный тип ShortString (эквивалент string[255]).

Для типа string память под строку выделяется динамически по мере надобности и ограничена только имеющейся в распоряжении программы доступной памятью. На этапе компиляции выделяется только 4 байта под адрес строки.

Длинная строка типа string отличается от короткой только механизмом работы с памятью ЭВМ. Рассмотрим этот механизм на примере:

var

s1, s2: string; //длинные строки

На этапе компиляции под переменные si и s2 типа string выделяется по 4 байта памяти, достаточные для хранения адресов первых байтов будущих строк. Фактически — это указатели на строки.

При выполнении оператора

s1:= 'Microsoft';

программа выделит в динамической памяти 14 байт = 9 байт (длина строковой константы) + 1 байт (терминальный ноль) + 4 байта (счетчик ссылок), поместит в переменную si адрес первого байта этого участка памяти и разместит в этом участке цепочку символов «Microsoft», завершив ее терминальным нулем и четырехбайтным счетчиком ссылок со значением 1.

Счетчик ссылок играет важную роль в механизме работы с памятью. С его помощью реализуется «кэширование» памяти. Например, при выполнении оператора

s2:= s1;

память для размещения значения переменной s2 не выделяется, в переменную s2 помещается содержимое указателя si, а счетчик ссылок в связанной с ним памяти (содержащей значение «Microsoft») увеличивается на единицу. Таким образом, оба указателя будут ссылаться на одну и ту же область памяти, счетчик ссылок которой содержит значение 2.

При изменении значения одной из переменных, например, оператором

s1:= 'Delphi';

счетчик ссылок уменьшается на единицу, под новое значение выделяется новая область памяти длиной 6+1+4=11 байтов, указатель на эту область помещается в si, а в саму память помещается значение «Delphi», терминальный ноль и содержащий единицу счетчик ссылок. Теперь переменные si и s2 ссылаются на разные участки памяти.

Выделенная для размещения строки типа string область памяти освобождается автоматически, если ее счетчик ссылок стал равен нулю.

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

Несмотря на разницу во внутреннем представлении, короткие строки типа ShortString и длинные строки типа string имеют для программиста одинаковые свойства. К элементам этих строк можно обращаться как к элементам массива символов, текущую длину которого можно определить с помощью функции Length. Индексация символов в массиве начинается с единицы.

Тип PChar — нуль-терминальнаяе строка, представляющая собой цепочку символов, завершающуюся символом #0. Максимальная длина такой строки ограничена имеющейся в распоряжении программы памятью и может быть очень большой. Тип PChar используется для прямого обращения к функциям API ОС.

Тип WideString. В 32-разрядной версии Windows используется 3 вида символов: однобайтный символ ANSI, двухбайтный символ и символ Unicode. Двухбайтный символ используется для отображения алфавита некоторых азиатских языков (младший байт — семибитный ASCII-код, старший указывает, как должен трактоваться этот код, т.е. каким символом он будет изображаться в документе или на экране). Unicode — символ кодируется словом. Международная комиссия по Unicode выработала соглашение, позволяющее представить символы всех языков мира. Двухбайтные символы и символы Unicode объявляются стандартным типом WideChar, а составленные из них строки — WideString. Все Windows программы, использующие OLE-технологию обмена строками, должны кодировать символы в соответствии с Unicode.

ВОЗНИКНОВЕНИЕ ИСКЛЮЧИТЕЛЬНЫХ СИТУАЦИЙ

В ходе выполнения программы могут возникать динамические ошибки, которые являются следствием неправильной работы инструкций, процедур, функций или методов программы, а также операционной системы. Динамические ошибки называют также ошибками времени выполнения (RunTime Errors).

Например, в операторе присваивания

ltog:=Count / Number;

во время выполнения программы возможно появление ошибки, если переменная Number будет принимать нулевое значение.

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

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

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

Внимание! При работе в среде Delphi при каждой исключительной ситуации среда перехватывает управление программой. Для отмены реакции среды на исключительную ситуацию необходимо вызвать опцию ToolsIDebugger Options и на странице Language Exceptions отменить переключатель Stop on Delphi Exceptions.

КЛАССЫ ИСКЛЮЧЕНИЙ

В Delphi базовым классом для всех исключений служит класс Exception. Свойство Message класса Exception содержит описание исключений. При возникновении исключения этот текст появляется в диалоговом окне сообщения об ошибке. Наиболее часто объект-исключение использует конструктор: Create(const Msg: string), который создает объект-исключение; строка Msg указывает текст для свойства Message создаваемого объекта.

От класса Exception порождены многочисленные дочерние типы, соответствующие часто встречающимся случаям ошибок ввода (вывода), распределения памяти и т.п.

Наиболее часто используемые классы исключений приведены в табл. П2.3.

Таблица П2.3

Основные классы исключений в Delphi

Класс

Обрабатываемое исключение

EAbort

«Тихая» (без сообщения) обработка любого исключения

EAbstractError

Программа пытается вызвать абстрактный метод

EAccessViolation

Программа пыталась обратиться к не принадлежащей ей области памяти или использует недействительный указатель

EAssertionFaild

Возбуждается отладочной процедурой Assert, когда тестируемое ею логическое выражение имеет значение False

EBitsError

Программа пыталась обратиться к свойству Bits объекта TBits с индексом меньше нуля или больше максимально допустимого значения

EControlC

Возникает при нажатии Ctrl-C при работе приложения в режиме консоли

EConvertError

Ошибки преобразования в функциях StrToInt или

StrTo Float

EExternalException

Возникла ошибка, не предопределенная в Delphi

ElnOutError

Ошибка в файловых операциях. Поле ErrorCode объекта этого класса содержит код ошибки:

  • 2 — Файл не найден; 3 — Неправильное имя файла;
  • 4 — Слишком много открытых файлов; 5 — Файл не доступен; 100 Достигнут конец файла (EOF)-, 101 — Диск переполнен', 106 — Ошибка ввода

EIntError

Любая ошибка в целочисленных вычислениях

EDiv ByZero

Попытка целочисленного деления на ноль

E Range Error

Присвоение целочисленной переменной значения, выходящего за пределы допустимого диапазона

EIntOverFlow

Целочисленное переполнение

EIntfCastError

Попытка недопустимого приведения типов в OLE объектах

ElnvalidCast

Программа пытается осуществить недопустимое преобразование типов с помощью оператора as

Класс

Обрабатываемое исключение

EInvalidGraphic

Программа пытается загрузить в контейнер изображение из файла недопустимого формата (допустимыми форматами являются растр, метафайл, курсор, пиктограмма)

EInvalidGraphic

Operation

Программа пытается выполнить недопустимую графическую операцию

EInvalidOperation

Не имеющий окна компонент пытается выполнить операцию, требующую дескриптора окна

EInvalidPointer

Попытка использовать недопустимый указатель (обычно nil)

EListError

Неверные действия программы по отношению к разного рода спискам (например, индекс меньше нуля)

EMathError

Любая ошибка в вычислениях с плавающей точкой

EInvalidOp

Ошибка в операциях (недопустимая инструкция, переполнение стека сопроцессора и т. п.)

EZeroDivide

Попытка вещественного деления на ноль

EOverFlow

Переполнение в операции с плавающей точкой

EUnderFlow

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

EMenuError

Ошибка при работе с меню, например, при добавлении элемента с идентификатором, ранее определенным в меню

EOutOfMemory

Программа запрашивает слишком большой объем памяти

EPrinter

Windows сообщила программе об ошибке принтера

EPrivilege

Программа пытается выполнить привилегированную операцию. Привилегированные операции могут выполняться только ядром Windows

EPropReadOnly

Программа пытается присвоить значение свойству, из которого можно только читать

EPropWriteOnly

Программа пытается прочитать свойство, предназначенное только для записи

EResNotFound

Программа не может найти указанный ресурс в файле ресурсов

EStreamError

Любая ошибка при работе с потоком данных

EFCreate Error

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

Класс

Обрабатываемое исключение

EFilerError

Программа пытается повторно зарегистрировать в потоке один и тот же класс

Е Read Error

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

EWrite Error

Ошибка записи в поток данных

EFOpenError

Ошибка открытия потока данных. Например, попытка открыть несуществующий файл

EStringListError

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

EThread

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

EVariantError

Ошибка при работе с типом Variant: недопустимое приведение типов; недопустимая операция; обращение к скалярной переменной как к варианту-массиву; индекс варианта-массива выходит из допустимых значений

EWin32Error

Ошибочное обращение к API-функции Windows. Свойство Message содержит номер ошибки и сообщение о ней

ИС EAbort названа фирмой Borland «тихой» и выделяется тем, что для нее обработка по умолчанию не подразумевает вывода никаких сообщений на экран. Такая ИС не создается системой: ее должен в нужных случаях и создавать, и обрабатывать сам программист.

Важно помнить, что ищется самый первый из, возможно, нескольких обработчиков, класс которого способен обрабатывать данное исключение. Если, например, в списке обработчиков первым стоит Abort, который может обрабатывать любое исключение, ни один из стоящих за ним обработчиков никогда не получит управление. Точно так же, если указан обработчик для класса EIntError, за ним бесполезно размещать обработчики EDivByZero, ERangeError или ElntOverflow.

ЗАЩИЩЕННЫЕ БЛОКИ КОДА

В языке Delphi обработку исключительных ситуаций можно вынести из основной части, выделив в составе блока кода:

  • 1) собственно реализацию алгоритма;
  • 2) обработчик ИС;
  • 3) заключительную часть, выполняющую необходимые действия по корректному завершению вне зависимости оттого, возникали или нет исключительные ситуации.

Для обработки ИС используются защищенные блоки кода.

Существует два основных вида таких блоков.

1. Блок защиты ресурсов try...finally используется, если необходимо возвратить выделенные программе ресурсы даже в случае аварийной ситуации. Синтаксис блока try...finally:

try

// операторы, выполнение которых может вызвать ошибку

finally

// операторы, выполняемые всегда, даже в случае ошибки

end;

Если в любом из операторов блока try возникает исключение, то управление передается операторам блока finally, которые называются кодом очистки (cleanup code; termination code). Если исключение не возникло, то выполняются все операторы обоих блоков.

Конструкция try .. finally не обрабатывает исключение, а выполняет действия, которые должны быть произведены даже в случае возникновения ошибки. Такими действиями могут являться, к примеру, освобождение оперативной памяти или закрытие файла.

Пример использования конструкции try .. finally:

var f: File;

begin

AssignFile(f, 'Somefile.ext');

try

// операторы, в которых возможна ошибка работы с файлом

Reset(f);

finally

CloseFile(f);

end;

end;

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

2. Блок try...except используется для реакции на конкретный тип ИС. Синтаксис блока:

try

// операторы, выполнение которых может вызвать ошибку

except

// операторы, которые должны быть выполнены

// только в случае ошибки

end;

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

Конструкция try .. except работает так: если в любой из инструкций блока try возникает исключение, то управление передается первой инструкции блока except. Если же исключение не возникло, то инструкции блока except не выполняются. При появлении исключения инструкции блока except могут ликвидировать исключительную ситуацию и восстановить работоспособность программы.

Пример использования конструкции try .. except:

var f: File;

begin

AssignFile(f, 'Somefile.ext');

try

// операторы, в которых возможна ошибка работы с файлом

Reset(f);

except

ShowMessage('Omn6Ka работы с файлом Somefile.ext');

CloseFile(f);

end;

end;

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

Блок except можно разбить на несколько частей с помощью конструкций on .. do, позволяющих анализировать класс исключения для его более удобной и полной обработки:

on Идентификатор: класс исключения do Оператор обработки ИС

else Операторы обработки исключения по умолчанию;

В инструкции on класс возникшего исключения сравнивается с указанным классом исключения. В случае совпадения классов выполняется оператор после слова do, реализующий обработку этого исключения.

Идентификатор (произвольное имя, заданное программистом) является необязательным элементом и может отсутствовать, при этом не ставится и разделительный знак двоеточия «:». Идентификатор — это локальная переменная класса исключения, которую можно использовать для доступа к объекту возникшего исключения внутри конструкции on .. do. Если класс возникшего исключения не совпадает с проверяемым классом, то выполняются операторы после слова else.

Блок else является необязательным и может отсутствовать.

Стандартная обработка подразумевает вывод на экран сообщения с указанием типа ошибки, имени модуля и адреса, где она имела место. Однако ее легко переопределить:

try

U:= 220.0; R:= 0; l:=U/R;

except

on EZeroDivide do ShowMessage('KopoTKoe замыкание!');

end;

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

on EObject: EClassName do EObject.Message := ...

Для стандартных классов такой прием фактически позволяет использовать единственное строковое свойство Message со стандартным сообщением об ошибке. Исключение составляет класс ElnOutError, в котором для программиста может представлять интерес целочисленное свойство ErrorCode с кодом ошибки ввода/вывода. Например:

try

Reset(f);

while not SeekEOF(f) do begin

// возможна ошибка работы с файлом

end;

Close(f);

except

on E: ElnOutError do

ShowMessage('npn выполнении файловой операции’+ #13#10’возникла ошибка №'+lntToStr(E.ErrorCode));

end;

Если в блоке except расположено несколько конструкций on .. do, то else располагается в конце блока и относится ко всей совокупности конструкций on .. do. Следующие после слова else операторы выполняются в том случае, если обработка исключений не была осуществлена ни в одном из операторов, расположенных в любой из конструкций do блока.

Если обработка ошибки (после do) требует записи нескольких операторов, то их необходимо заключить в блок begin ... end.

После else можно записывать любое количество операторов.

Пример. В поля Edit 1 и Edit2 вводятся два целых числа. При нажатии кнопки btDiv выполняется преобразование этих чисел из текстового формата в числовой, после чего первое число делится на второе, а результат помещается в поле Edit3. Эти действия могут вызвать ошибки, поэтому они помещены в блок try. Если возникает исключение, оно анализируется в блоке except. Проверяются варианты: EDivByZero и EConvertError. При возникновении одного из прогнозируемых исключений выдается сообщение об ошибке и в поле результата выводится строка «Ошибка!». Любое другое исключение не будет распознано. В этом случае выполняются операторы блока else и выдается сообщение о неопознанной ошибке.

procedure TForml .btDivClick(Sender: TObject);

var

x, y, res: Integer;

begin

try

x:=StrTolnt(Edit1 .Text); //возможна ошибка преобразования

y:=StrTolnt(Edit2.Text); //возможна ошибка преобразования

res:=x div у; //возможна ошибка деления на ноль

Edit3.Text:=lntToStr(res);

except

on EDivByZero do begin //проверка целочислен, деления на ноль ShowMessage(TlonbiTKa деления на ноль!');

Edit3.Text:='Oi±iH6Ka!'; //вывод текста «Ошибка!» в поле Edit3

end;

on EConvertError do begin//проверка ошибки преобразования ShowMessage('OflHO из чисел содержит недопустимые символы!'); Edit3.Text:='Oi±iH6Ka!'; //вывод текста «Ошибка!» в поле Edit3

end;

else

ShowMessage('Omn6Ka не идентифицирована'); Edit3.Text:='Omn6Ka!'; //вывод текста «Ошибка!» в поле Edit3

end;

end.

Конструкции try могут быть вложенными и размещаться одна в другой. При этом внешняя и внутренняя конструкции могут иметь любой из двух рассмотренных видов. Обязательным условием является то, что внутренний блок должен полностью размещаться во внешнем блоке. Можно защищать каждый вид ресурсов системы в отдельном блоке, вкладывая один блок защиты ресурсов try...finally в другой. Можно также вкладывать друг в друга обработчики try... except, предусмотрев в каждом специфическую реакцию на ту или иную ошибку, например:

procedure TfmDegree.bbResultClick(Sender: TObject);

// Возведение числа в степень

var

х, у, z : Extended;

begin

try

x:=StrToFloat(edBase.Text); //получение основания степени

try

y:=StrToFloat(edPower.Text); //получение показателя степени

try

z:=exp(y*ln(x)); //возведение в степень

edResult.Text:=FloatToStr(z);

except

on ElnvalidOp do

ShowMessage('OcHOBaHne не может быть отрицательным');

on EZeroDivide do

ShowMessage('OcHOBaHne не может быть равным нулю');

end;

except

on EConvertError do

ShowMessage(TloKa3aTenb содержит недопустимые символы');

end;

except

on EConvertError do

ShowMessage('OcHOBaHne содержит недопустимые символы');

end;

end;

ВЫЗОВ ИСКЛЮЧЕНИЙ

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

raise ClassException.Construct

Здесь ClassException является классом исключения, на основе которого создается объект-исключение, а конструктор Construct выполняет создание объекта-исключения. Через этот метод объекту-исключению передается информация о виде исключения. Для создания объектов-исключений чаще всего используется конструктор Create классов исключений. Строковый параметр обращения к конструктору запоминается в поле FMessage и становится доступен с помощью свойства Message объекта.

Примеры генерации исключений в секции try .. except:

// исключение класса Exception raise Exception.Create('HoBaB ошибка!')

// ошибка ввода-вывода

raise ElnOutError.Create('HeBepHaB файловая операция!');

Обработчик исключения EInOutError в секции except.. end: on Е: EInOutError do ShowMessage(E.Message);

именует объект идентификатором E и с помощью стандартной процедуры ShowMessage показывает значение его свойства Message в окне.

Пример генерации и обработки исключения:

procedure TfmPassWord.btOKCIick(Sender: TObject);

begin

try

if edPassWord.Text <> Key

then raise Exception.Create('flocTyn запрещен!');

fmWork.Show;

except

on E: Exception do begin

ShowMessage (E.Message); Close;

end;

end;

end;

В секции try обработчика нажатия кнопки btOK формы ввода пароля fmPassWord введенный в поле edPassWord текст сравнивается с переменной Кеу, содержащей строку пароля. Если они не совпадают, то генерируется исключение Exception и управление передается в блок except. Расположенный после инструкции raise оператор вывода на экран формы fmWork не выполняется.

Обработка исключения заключается в выводе соответствующего сообщения об ошибке и закрытия формы ввода пароля, что приведет к завершению работы приложения, если эта форма — основная.

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

Инструкцию raise, кроме генерации исключения, можно использовать и для более удобной обработки уже возбужденных исключений (например, добавить к стандартному обработчику ошибок собственные действия). В этом случае ее синтаксис совсем прост:

raise;

Если зарезервированное слово raise встретилось в секции frnally .. end или except.. end, то считается, что данный защищенный блок на текущем уровне вложенности завершил свою работу и управление передается вышестоящему уровню вложенности защищенного блока.

Если других уровней обработки нет, то исключение будет перехвачено обработчиком по умолчанию, который выведет собственное сообщение об ошибке.

Пример возбуждения исключения для добавления собственного сообщения об ошибке:

try

// Возможна ошибка EMathError

except

on EMathError do begin

ShowMessage('Bo3HHKna ошибка в вычислениях!');

raise; // вызов вышестоящего или стандартного обработчика

end;

end;

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

СОЗДАНИЕ КЛАССОВ ИСКЛЮЧЕНИЙ

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

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

type

EWrongPassword = class (Exception) //тип ИС - неверный пароль

end;

То, что в таком типе ничего не переопределено, не столь важно — главное, что в обработчике ИС можно отследить именно его.

Затем новые или измененные элементы класса, если они описаны, необходимо реализовать в разделе implementation и в нужный момент инициировать новое исключение инструкцией raise.

try

// Сравнение введенного текста с паролем if edPassword.Text <>Кеу

then

// Возбуждение исключения

// и запись сообщения для него в его свойство Message raise EWrongPassword.CreatefflocTyn к данным невозможен!');

except

// При возникновении исключения Е типа EWrongPassword

// вывод сообщения из его свойства Message

on Е: EWrongPassword do ShowMessage(E.Message);

end;

МОДУЛИ В ОЕЬРН1

Модуль в Ое1рЫ состоит из следующих частей:

  • 1) заголовок модуля;
  • 2) секция интерфейса;
  • 3) секция реализации;
  • 4) секция инициализации;
  • 5) секция деинициализации.

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

Заголовок модуля состоит из зарезервированного слова unit и идентификатора. Идентификатор модуля должен быть уникальным. Модуль должен быть помещен в файл, имя которого совпадает с именем модуля, а его расширение должно быть .pas. После компиляции файл с модулем получает расширение .dcu.

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

Секция начинается зарезервированным словом interface. Далее могут размещаться:

  • 1) предложение uses, в котором перечисляются имена модулей, используемых данным модулем;
  • 2) раздел описания констант;
  • 3) раздел описания типов;
  • 4) раздел описания переменных;
  • 5) раздел описания процедур и функций.

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

Любой из подразделов может отсутствовать. Допустима даже пустая секция интерфейса, но модуль с такой секцией никому не нужен.

В разделе объявления процедур и функций указываются лишь заголовки подпрограмм. Сами подпрограммы (их блоки) размещаются в секции реализации.

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

Назначение секции реализации — реализация подпрограмм, заявленных в секции интерфейса.

Секция начинается зарезервированным словом implementation. Далее могут размещаться:

  • 1) предложение uses, в котором перечисляются имена модулей, используемых данным модулем, но не упомянутые в предложении секции uses интерфейса;
  • 2) раздел описания меток;
  • 3) раздел описания констант;
  • 4) раздел описания типов;
  • 5) раздел описания переменных;
  • 6) раздел описания процедур и функций.

Структурно секция реализации отличается от секции интерфейса только тем, что разрешено наличие раздела описаний меток. Как и в секции интерфейса:

  • 1) порядок следования разделов описаний может быть произвольным;
  • 2) возможны повторения разделов;
  • 3) действует правило: используемое сейчас должно быть описано выше;
  • 4) любой из подразделов может отсутствовать;
  • 5) допустима пустая секция реализации.

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

Раздел описания подпрограмм обязательно должен содержать описания подпрограмм, заявленных (описанных) в секции интерфейса. В описаниях этих подпрограмм допустимо использовать сокращенные заголовки без списков формальных параметров. Описания каждой подпрограмм обязательно содержит ее блок (тело), состоящее из раздела описаний и раздела операторов.

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

Важно понимать, что все метки, константы, типы, переменные и подпрограммы, описанные в секции реализации, кроме подпрограмм из секции интерфейса, являются локальными элементами данного модуля, они не видны из других программ и модулей, они создаются только с одной целью — реализовать подпрограммы секции интерфейса!

Секция инициализации — факультативная часть модуля. В ней размещаются операторы, которые выполняются в начале работы той программы, которая использует данный модуль. Если программа использует несколько модулей, то содержимое их секций инициализации выполняется в порядке перечисления имен модулей в предложении uses. Эта секция начинается зарезервированным словом initialization, например:

initialization

Assign(f1, 'File1.dat');

Секция деинициализации начинается словом finalization и является необязательной. В этом разделе размещаются операторы, выполняемые при завершении работы программы, использующей данный модуль. Если программа использует несколько модулей, то содержимое их секций деинициализации выполняется в порядке, обратном порядку их перечисления в предложении uses.

В конце модуля размещается слово end. (с точкой).

Использование модуля в Delphi ничем не отличается от его использования в Турбо Паскале.

Добавление модуля к проекту: в меню File выполнить команду New ?ив появившемся списке вновь создаваемых элементов проекта выбрать Unit (модуль). В окне кода проекта появится новая страница с заготовкой модуля, а в файл проекта автоматически будет вставлена ссылка на этот модуль. Новый модуль необходимо сохранить в нужной папке под нужным именем. Обычно модуль хранится в той же папке, что и проект.

Программу, использующую модули, можно компилировать либо с помощью команды меню Project|Compile <имя проекта> (клавиши Ctrl+F9) либо Project|Build <имя проектах

При использовании команды Project|Compile наряду с компиляцией главной программы компилируются и те используемые программой модули, у которых к моменту компиляции был изменен текст.

При использовании команды Project|Build все используемые программой модули перекомпилируются безусловно.

ДИНАМИЧЕСКИЕ ПЕРЕМЕННЫЕ

Статические и динамические переменные. Переменные, объявляемые в разделе var главной программы, называются статическими переменными потому, что они являются составной частью программы и существуют в виде ячеек памяти в течение всего времени пребывания программы в ЭВМ. Размещаются статические переменные в сегменте данных программы, память для них выделяется на этапе компиляции, а их физические адреса устанавливаются на этапе загрузки.

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

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

Указатели. Работа с динамической памятью осуществляется с помощью величин, называемых указателями. Значение указателя — адрес участка памяти, в котором могут располагаться переменная или подпрограмма. Компилятор отводит под указатель 4 байта памяти.

Различают типизированные и нетипизированные (нейтральные) указатели.

Синтаксическая форма описания типизированного указателя:

ABaseType,

где Л — синтаксический атрибут (символ каре); BaseType — идентификатор типа, называемого базовым типом (любой тип Delphi).

В Delphi действует принцип — любой идентификатор может использоваться только после его определения; для идентификатора базового типа сделано исключение — его можно определять после использования в описании указателя.

Указанное исключение позволяет создавать списковые структуры данных в динамической памяти ЭВМ.

Описатель ЛBaseType может использоваться и в разделе программы type для описания идентификатора типа, и в разделе var для описания конкретных переменных-указателей.

Пример. Описание типизированных указателей.

type

tArr=array[1..10] of Real; pArr=AtArr;

var

p1: pArr; p2: AReal; p3: Alnteger;

Типизированные указатели могут использоваться для хранения адресов объектов только базового типа. Нейтральные указатели могут использоваться для хранения адресов объектов любого типа. Они описываются с помощью ключевого слова Pointer.

Пример. Описание нейтральных указателей.

var

р1, р2: Pointer;

Операция получения адреса является унарной, кодируется символом @ (коммерческое ИЛИ, транскрипция названия символа — aet) и имеет следующую синтаксическую форму:

где X —идентификатор переменной или подпрограммы.

Конструкция @Х представляет собой выражение, тип которого определяется по следующему правилу:

  • 1) если X — имя переменной, то при использовании опции компилятора {$Т—} выражение @Х имеет тип нейтрального указателя (Pointer); при использовании опции {$Т+} выражение @Х имеет значение типизированного указателя, базовый тип которого совпадает с типом переменной X;
  • 2) если X — имя подпрограммы, то выражение @Х имеет тип нейтрального указателя (Pointer) независимо от установки опции {$Т}.

Функции и процедуры для работы с динамической памятью

function Addr(var X): Pointer;

Возвращает адрес переменной или подпрограммы с именем X. В отличие от операции @ функция Addr(X) всегда возвращает адрес только в виде нейтрального указателя, независимо от установки опции {$Т}.

procedure GetMem(var р: Pointer; Size: Word);

Выделяет в куче непрерывный участок (блок) памяти размером Size байт, адрес начального байта этого блока присваивается указателю р.

procedure FreeMem(var р: Pointer; Size: Word);

Освобождает блок памяти размером Size байт, адрес которого хранится в указателе р; значение указателя р, согласно спецификации языка, становится неопределенным; фактически значение р не изменяется.

procedure New(var р: Pointer);

Создает в куче динамическую переменную того типа, на которую ссылается указатель р, адрес начального байта этой переменной присваивается указателю р; заметим, что обращение к данной процедуре эквивалентно обращению GetMem(p, SizeOf(pA)).

Напомним, что функция SizeOf(X) возвращает размер переменной X в байтах.

procedure Dispose(var р: Pointer);

Освобождает блок памяти, занимаемый динамической переменной, адрес которой хранится в указателе р; значение указателя р, согласно спецификации языка, становится неопределенным; фактически значение р не изменяется.

Заметим, что обращение к данной процедуре эквивалентно обращению FreeMem(p,SizeOf(pA)).

Процедуры GetMem и FreeMem, New и Dispose являются парными.

Присвоить значение указателю р можно двумя способами:

1) с помощью оператора присваивания

р:-е

где е — выражение, значение которого представляет собой адрес, то есть выражение типа указателя;

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

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

В Delphi определен идентификатор nil константы, называемой пустым адресом, или адресным нулем. Константа nil не адресует никакой объект. По присваиванию она совместима со всеми указателями: как нейтральными, так и типизированными.

Организация ссылок

Ссылка — это способ доступа к объекту программы и соответствующая синтаксическая конструкция.

В Delphi реализованы три формы ссылок на переменные:

  • 1) имя переменной;
  • 2) приведение типа;
  • 3) типизированный указатель с последующим квалификатором разыменования.

Предметом изучения в настоящей теме являются ссылки в форме переменной с последующим квалификатором разыменования, имеющие следующую синтаксическую форму

РЛ

где р — типизированный (не нейтральный!) указатель.

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

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

var

х: Real; р: AReal;

begin

р:= @х; //теперь рЛ и х- синонимы

рЛ:=7.25; //тоже самое, что х:=7.25

 
Если Вы заметили ошибку в тексте выделите слово и нажмите Shift + Enter
< Пред   СОДЕРЖАНИЕ   След >
 

Популярные страницы