Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

[ Миронченко ] Императивное и объектно-ориентированное програмирование на Turbo Pascal и Delphi

.pdf
Скачиваний:
68
Добавлен:
25.04.2014
Размер:
3.16 Mб
Скачать

341

Для того чтобы обрабатывать исключение, в 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

343

24:property Mass[Ind:integer]:integer read GibZahl write StellZahl;

25:function ZurZeile:string; //преобразует в MasKLass строку

26:end;

27:

28:procedure MasKlass.StellZahl(Index:integer;G:integer);

29:begin

30:if (index>n) or (index<1) then //если индекс вышел за границы

массива

31:raise ESchlechteIndex.Create('Индекс вышел за границу

массива');

32:if G>=0 then

33:R[Index]:=G

34:else

35:raise ENegativeZahl.Create('В MasKlass не могут быть

отрицательные числа'); 36: end;

37:

38:function MasKlass.GibZahl(Index:integer):integer;

39:begin

40:if (index>n) or (index<1) then //если индекс вышел за границы

массива

41:raise ESchlechteIndex.Create('Индекс вышел за границу

массива')

42:else

43:result:=R[index];

44:end;

45:

46:function MasKlass.ZurZeile:string;

47:var

48:i:integer;

49:begin

50:Result:='';

51:for i:=1 to n do

52:Result:=Result+IntToStr(R[i])+' ';

53:end;

54:

55:var

56:MK:MasKlass;

57:i:integer;

58:begin

59:MK:=MasKlass.Create;

60:for i:=1 to n+3 do

61:try

62:MK.Mass[i]:=i - 2;

63:except

64:on ESchlechteIndex do //если вышли за границы массива

65:ShowMessage('Плохой индекс');

66:on ENegativeZahl do //если в массив пытаются записать

отрицательное число.

67:MK.Mass[i]:=0;

68:end;

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;

346

41:for i:=1 to n do

42:begin

43:new(R);

44:r^.x:=random(n);

45:

repeat

//знаменатель не должен быть равен 0

46:r^.y:=random(n);

47:until r^.y<>0;

48:T.Add(R); //добавляем элемент в список

49:end;

50:end;

51:

52://Функция сравнения для элементов списка

53:function Vergleiche(p1,p2:pointer):integer;

54:begin

55:if pRat(p1).x*pRat(p2).y>pRat(p1).y*pRat(p2).x then

56:result:=1

57:else

58:if pRat(p1).x*pRat(p2).y=pRat(p1).y*pRat(p2).x then

59:result:=0

60:else

61:result:=-1;

62:end;

63:

64:procedure TForm1.Button1Click(Sender: TObject);

65:var

66:i:integer;

67:begin

68:FulleLst(Lst,100);

69:Memo1.Lines.Add(IntToStr(Lst.Count));

70:

71:for i:=1 to Lst.Count do

72:Memo1.Lines.Add(IntToStr(i)+': '+IntToStr(pRat(Lst.Items[i- 1])^.x)+ '/'+ IntToStr(pRat(Lst.Items[i-1])^.y));

74:Memo1.Lines.Add('Отсортированный массив');

75:Lst.Sort(Vergleiche); //Сортируем список с помощью функции

Vergleiche

76:for i:=1 to Lst.Count do

77:Memo1.Lines.Add(IntToStr(i)+': '+IntToStr(pRat(Lst.Items[i- 1])^.x)+ '/' +IntToStr(pRat(Lst.Items[i-1])^.y));

78:end;

79:end.

17.15.Процессы и потоки

Напомню основные определения:

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

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

Поток может находиться в следующих состояниях:

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.Модернизировать калькулятор так, чтобы он мог работать с вещественными числами (в любой ССч). Также добавьте дополнительные операции: деление, возведение в степень а также вычисление стандартных функций: синуса, косинуса и т.д.