[ Миронченко ] Императивное и объектно-ориентированное програмирование на Turbo Pascal и Delphi
.pdf341
Для того чтобы обрабатывать исключение, в Delphi введен следующий оператор
try:
1-я версия try:
try //операторы
except
//обработчики исключений else
//операторы end;
Часть программы, которая подозрительна на возникновение исключений, помещается после слова try. Если все операторы отработали нормально, то программа переходит к оператору следующему за оператором try. Если же возникло исключение, то просматриваются поочередно обработчики исключений, перечисленные в разделе except, пока не найдется такой обработчик, который сможет обработать исключение, которое возникло. Если ни один из перечисленных классов не способен обработать исключение, то выполняются операторы секции else.
2-я версия try:
try //операторы
finally //операторы
end;
Если в промежутке от try до finally возникло исключение, то все последующие операторы пропускаются и управление передается в секцию finally. Если же все операторы были успешно выполнены, то управление все равно передается операторам в секции finally.
Кстати, внутри обработчиков исключений нельзя использовать оператор goto.
Пример 6: Использование исключений.
Создайте форму с 2-мя текстовыми полями (типа TEdit) и одной кнопкой. По нажатию кнопки во второе поле должно записываться значение квадратного корня из числа, записанного в первом поле.
При таких вычислениях может возникнуть 2 исключения: если строка, написанная в первом поле, вообще не является числом (класс EConvertError), либо было введено отрицательное число, следовательно, операция вычисления квадратного корня недопустима и возникнет исключение EInvalidOp.
Обработчик нажатия на кнопку будет иметь следующий вид:
procedure TForm1.Button1Click(Sender: TObject); var
x:real; begin
try
x:=StrToFloat(edit1.text); //Может возникнуть EConvertError edit2.text:=FloatToStr(sqrt(x)); //Может возникнуть EInvalidOp
except
342
on EConvertError do showMessage('неверно введено число');
on EInvalidOp do showMessage('отрицательное число');
end;
end;
Как видите, обработка исключения может быть не только суровой необходимостью, но и благом: мы могли бы обойтись и без использования исключений, но для этого надо было бы самостоятельно проверить правильность ввода строки – т.е. проверить, было ли действительно введено действительное число, а для этого нам пришлось бы фактически писать собственную процедуру перевода строки в число, которая при неверно сформированной строке выполняла необходимые нам действия, а это значительно дольше, чем обработать исключение EConvertError.
Можно создавать и собственные классы исключений. Зачем они нужны, если ОС все равно сама не сможет генерировать исключения таких классов? Лишь затем, чтобы самостоятельно их вызывать и затем обрабатывать.
В следующем примере мы создадим класс, который будет представлять собой «защищенный» массив. В обычных массивах можно обращаться к элементам, индексы которых не входят в диапазон, который объявлен для данного массива. При этом могут портиться данные, которые находятся в этих байтах. Мы с вами создадим класс, у которого есть свойство-массив, причем при выходе за границы массива или при попытке присвоить элементу массива отрицательное число будут генерироваться исключения.
Пример 7: Обработка пользовательских исключений.
1 : program RaiseExc;
2 :
3 : {$APPTYPE CONSOLE}
4 :
5 : uses
6 : SysUtils,
7 : Dialogs;
8 :
9 : const 10: n=5; 11:
12:type
13:ESchlechteIndex = class(Exception);//выход за границы массива
14:ENegativeZahl = class(Exception); //отрицательное число
15:
16: Mas = array [1..n] of integer; 17:
18:MasKlass = class
19:private
20:R:Mas;
21:procedure StellZahl(Index:integer;G:integer);
22:function GibZahl(Index:integer):integer;
23:public
344
69:writeln(MK.ZurZeile);
70:readln;
71:end.
17.14. Класс TList
Класс TList и его наследники часто используются в компонентах, поэтому мы рассмотрим его с вами. TList – это класс, который моделирует список с помощью динамического массива нетипизированных указателей. Это означает, что хотя по своей сути это – массив, но методы класса реализованы так, как будто это на самом деле список.
Вспомним особенности использования динамических массивов в памяти. При добавлении нового элемента к массиву может случиться, что непосредственно за массивом в памяти нет свободного места для размещения нового элемента. Тогда надо будет найти в динамической памяти область, достаточного размера, чтобы она могла вместить массив вместе с новым элементом, затем скопировать в эту область весь массив из предыдущего места хранения, добавить к нему новый элемент, а старое место пребывания вернуть в кучу. Как вы понимаете, такая процедура занимает достаточно много времени, поэтому класс TList кроме базовых возможностей работы с динамическим массивом обладает рядом функций, позволяющих лучше использовать память.
Во-первых, класс TList всегда занимает в куче места не меньше, чем его количество элементов. Ключевое слово в предыдущей фразе – «не меньше», т.к. можно зарезервировать память «про запас».
Общее количество элементов, занятых массивом (с теми, которые запасены впрок), находится в свойстве Capacity. Количество занятых элементов находится в свойстве Count.
Получить доступ к любому элементу класса можно с помощью свойства property Items[Index: Integer]: Pointer;
Основные методы:
procedure Clear; virtual;
Удаляет все элементы в списке.
procedure Delete(Index: Integer);
Удаляет элемент в списке с заданным индексом. При этом сама память, связанная с элементом не освобождается (за это отвечает свойство capacity).
function Add(Item: Pointer): Integer;
Добавляет элемент Item в конец списка и возвращает его индекс.
procedure Insert(Index: Integer; Item: Pointer);
Вставляет элемент Item на место Index (при этом все элементы массива, начиная с места Index сдвигаются на 1 вправо).
procedure Sort(Compare: TListSortCompare);
type TListSortCompare = function (Item1, Item2: Pointer): Integer;
Сортирует массив с помощью функции типа TListSortCompare. Функция Compare должна возвращать:
положительное число, если Item1>Item2 0, если Item1=Item2
отрицательное число, если Item1<Item2
345
Пример 8: Использование TList.
Смысл программы в том, что создается массив записей, каждая из которых представляет собой дробь. Процедура
procedure FulleLst(var T:TList;n:integer);
заполняет список n случайными значениями. Заметьте, что перед заполнением списка устанавливается значение свойства capacity (строка 39). Это надо как раз для того, чтобы не приходилось много раз расширять массив.
Для сортировки списка будет использоваться (строка 75) функция function Vergleiche(p1,p2:pointer):integer;
1 : unit Haupt;
2 :
3 : interface
4 :
5 : uses
6 : Windows, Messages, SysUtils, Variants, Classes, Graphics,
7 : Controls, Forms, Dialogs, StdCtrls;
8 :
9 : type
10:TForm1 = class(TForm)
11:Memo1: TMemo;
12:Button1: TButton;
13:procedure Button1Click(Sender: TObject);
14:private
15:{ Private declarations }
16:public
17:{ Public declarations }
18:end;
19:
20:type
21:Rat = record
22:x,y:integer;
23:end;
24:
25: pRat=^Rat; 26:
27:var
28:Form1: TForm1;
29:Lst:TList;
30:implementation
32:{$R *.dfm}
33:procedure FulleLst(var T:TList;n:integer);
34:var
35:R:pRat;
36:i:integer;
37:begin
38:T:=TList.Create; //создаем список
39:T.Capacity:=n; //устанавливаем его длину
40:randomize;
347
1.Существующий
2.Несуществующий
3.Работающий
4.Приостановленный (т.е. он существует, но в данный момент не выполняется).
Для работы с потоками в Delphi есть специальный класс TThread. В этом классе есть свойство
property Suspended: Boolean;
которое показывает, приостановлен ли поток. Если Suspended = true, то поток приостановлен. В противном случае он – работающий.
Сэтим свойством можно работать напрямую, устанавливая ему нужное значение,
аможно использовать и следующие процедуры:
procedure Resume; {переводит поток из состояния «приостановленный» в состояние «работающий»}
procedure Suspend; {приостанавливает работу потока}
Чтобы продемонстрировать полезность потоков, в примере мы будем сортировать большой массив чисел методом пузырька (конечно, большие массивы пузырем никто не сортирует, но нам сейчас главное – чтобы процедура работала долго). Если не выделить для сортировки отдельный поток, то пользователь может надолго утратить контроль за окном. А выделение дополнительного потока эту проблему легко снимет.
Основной модуль HauptFlut содержит 2 процедуры работы с массивами – случайного заполнения и сортировки. При щелчке на кнопку Button2 будет производиться сортировка массива без создания нового потока. При щелчке на кнопку Button1 будет создаваться дополнительный поток.
Класс потока можно создать, выбрав при создании нового файла ThreadObject (естественно, можно это сделать просто написав нужный класс).
Класс потока написан в модуле FlutSort. Любой поток должен быть наследником класса TThread. Основной подпрограммой потока является процедура Execute. В самом TThread эта процедура объявлена как абстрактная и, следовательно, должна переопределяться во всех потомках этого класса.
Т.к. все потоки находятся в одном и том же адресном пространстве, то надо каким-то образом синхронизировать работу потоков с глобальными переменными.
Для синхронизации работы потоков в классе TThread служит процедура Synchronize, единственным параметром которого является процедура без параметров.
Вспомогательный поток (объект класса Sorting) в процедуре Execute сортирует глобальный массив и потом записывает отсортированный массив в Memo1. Для того, чтобы дописывать строки используется процедура Schr, которая является параметром процедуры Synchronize.
Пример 9: Сортировка в дополнительном потоке.
1 : unit Hauptflut;
2 :
3 : interface
4 :
5 : uses
6 : Windows, Messages, SysUtils, Variants, Classes, Graphics,
7 : Controls, Forms,Dialogs, StdCtrls,
348
8 : FlutSort; //модуль, в котором хранится класс-наследник TThread 9 :
10:type
11:TForm1 = class(TForm)
12:Memo1: TMemo;
13:Button1: TButton;
14:Memo2: TMemo;
15:Button2: TButton;
16:procedure FormCreate(Sender: TObject);
17:procedure Button1Click(Sender: TObject);
18:procedure Button2Click(Sender: TObject);
19:private
20:{ Private declarations }
21:public
22:{ Public declarations }
23:end;
24:
25:const
26:n=40000;
27:type
28:Mas=array [1..n] of integer;
29:var
30:Form1: TForm1;
31:M:Mas;
32:Sort:Sorting; //поток
33:
34:implementation
35:{$R *.dfm}
36:
37:procedure RandMas(var M:Mas);
38:var
39:i:integer;
40:begin
41:randomize;
42:for i:=1 to n do
43:M[i]:=random(1000000);
44:end;
45:
46:procedure BlaseSort(var M:Mas);
47:var
48:i,j,tmp:integer;
49:begin
50:for i:=n downto 1 do
51:for j:=1 to i-1 do
52:if M[j]<M[j+1] then
53:begin
54:tmp:=M[j];
55:M[j]:=M[j+1];
56:M[j+1]:=tmp;
57:end;
58:end;
59:
349
60:procedure TForm1.FormCreate(Sender: TObject);
61:begin
62:Memo1.Left:=0;
63:Memo1.Top:=0;
64:Memo1.Width:=Form1.Width div 2;
65:Memo1.Height:=Form1.Height div 2;
66:RandMas(M);
67:Sort:=Sorting.Create(true); //Создаем объект Sort
68:end;
69:
70:procedure TForm1.Button1Click(Sender: TObject);
71:begin
72:try //поток Sort может не существовать
73:if Sort.Suspended=false then //Если поток работает
74:Sort.Suspend {Останавливаем поток}
75: |
else |
//если сейчас остановлен (но существует !) |
76:Sort.Resume; {Возобновляем работу потока}
77: |
except |
//если не существует |
78:on EThread do
79:// ShowMessage('Поток уже завершил работу')
80:end;
81:
82: end; 83:
84://просто сортируем массив, не выделяя дополнительного потока
85:procedure TForm1.Button2Click(Sender: TObject);
86:begin
87:BlaseSort(M);
88:end;
89:
90: end.
------------------------- Класс потока ----------------------------
1 : unit FlutSort;
2 :
3 : interface
4 :
5 : uses
6 : Classes,SysUtils;
7 :
8 : type
9 : Sorting = class(TThread)
10:private
11:{ Private declarations }
12:protected
13: |
s:string; |
//строка, которая должна дописываться в Мемо. |
14:procedure Execute; override;
15:procedure Schr;
16:end;
17:
18: implementation 19:
20: uses
350
21:HauptFlut; //основной модуль (присоединить в интерфейсной
части нельзя)
22:{
23:Schr - процедура, с помощью которой проводится синхронизация
24:с основным потоком процесса.
25:}
26:procedure Sorting.Schr;
27:begin
28:Form1.Memo1.Lines.Add(S);
29:end;
30:
31://Execute - основная процедура потока.
32:// Когда она заканчивает свою работу, поток исчезает.
33:procedure Sorting.Execute;
34:var
35:i,j,tmp:integer;
36:begin
37:for i:=n downto 1 do
38:for j:=1 to i-1 do
39:if M[j]<M[j+1] then
40:begin
41:tmp:=M[j];
42:M[j]:=M[j+1];
43:M[j+1]:=tmp;
44:end;
45:s:='';
46:i:=1;
47:while i<=n do
48:begin
49:for j:=1 to 5 do //формируем строку
50:begin
51:if (i>n) then
52:break;
53:s:=s+IntToStr(M[i])+' ';
54:inc(i);
55:end;
56:Synchronize(Schr); {дописываем строку в поле}
57:s:='';
58:end;
59:end;
60:end.
Задачи
1.Изменить программу с убегающей кнопкой так, чтобы она не могла выходить за границы формы.
2.Модернизировать калькулятор так, чтобы он мог работать с вещественными числами (в любой ССч). Также добавьте дополнительные операции: деление, возведение в степень а также вычисление стандартных функций: синуса, косинуса и т.д.