- •Windows -приложение
- •Среда программирования
- •Встроенный отладчик
- •Использование графики
- •Графические данные и палитра
- •Сохранение проекта
- •Структура приложения
- •Структура модуля
- •Простые типы
- •Символьные типы
- •Логические типы
- •Тип перечень
- •Составной оператор
- •Оператор if
- •Оператор цикла for
- •Оператор цикла while
- •Оператор цикла repeat
- •Пример приложения 6
- •Пример приложения 7
- •Статические массивы
- •Динамические массивы
- •Оператор with
- •Идентичность типов
- •Совместимость типов
- •Преобразование типов
- •Операторы обработки исключительных ситуаций
- •Рекурсия
- •Процедура exit
- •Директивы подпрограммы
- •Класс как объектный тип
- •Наследование
- •Операции is и as
- •Типы ссылки на класс
- •Типизированные файлы
- •Файлы без типа
- •Пример приложения 17
- •Компонент tmainmenii
- •Двунаправленные списки
- •Потоки данных
- •Пример приложения 22
- •Интерфейс drag and drop
- •Пример приложения 24
- •С файлами
- •Пример приложения 26
- •Программные потоки
- •Приоритеты потоков
- •Класс tthread
- •Проблемы синхронизации потоков
Двунаправленные списки
Структура двунаправленного связанного списка приводится на рис. 42. Данная структура соответствует следующему объявлению:
Type PStroka = AStroka; Stroka = record
Info:string;
Pred,Sled: PStroka; End;.
Рис. 42
Для построения связей между отдельными элементами списка используются два указателя: Pred - связь с предыдущим элементом и sled - связь с последующим элементом. Соответственно имеет место ограничение связей элементов списка и слева, и справа с помощью пустого указателя nil. Построим список из трех элементов, как в Предыдущем случае, и при условиях предыдущей задачи.
New(Spisok);
SpisokA.Info:=S1;
SpisokA.Pred:=nil;
Hew (P) ;
PA.Info:=S2;
PA.Pred:=Spisok;
SpisokA.Sled:=P;
New{P) ;
PA.Info:=S3;
PA .Pred:=SpisokA.Sled;
SpisokA.SledA.Sled:=P;
PA.sled:=nil;
P:=nil;.
{Начало списка}
{Запись строки SI в список}
{Ограничение связей списка слева}
{Новый элемент списка}
{Запись строки S2 в элемент Р}
{Связь с предыдущим элементом}
{Подключение элемента два в список}
{Третий элемент списка}
{Запись строки S3 в элемент Р}
{Связь с предыдущим элементом}
{Подключение элемента 3 в список}
{Конец списка}
На основе двунаправленного списка возможно формирование кольцевых связанных списков, когда самый первый элемент списка ссылается на последний элемент, а последний - на первый элемент.
154
155
Тогда получим структуру
дерева, представленную на рис. 43.
Структуру бинарного
дерева синтаксически можно объявить
следующим образом:
Предполагается, что элементы в списках могут добавляться и извлекаться в произвольном порядке. Существуют списки с заранее заданными процедурами добавления и извлечения элементов. Это стеки и очереди. Очередью называется однонаправленный или двунаправленный связанный список с процедурой работы FIFO - First In, First Out (первым пришел, первым ушел). В списках типа очередь элемент добавляется в конец списка, а извлекается из начала списка. Стек - связанный список с процедурой работы LIFO - Last In, First Out (последним пришел, первым ушел). Элементы стека добавляются в конец списка и извлекаются из конца списка.
Идентифицируется очередь с помощью двух указателей: запоминается начало очереди и конец списка. Стек можно идентифицировать одним указателем, помечая дно стека с помощью nil.
БИНАРНЫЕ ДЕРЕВЬЯ
Содержательную информацию часто требуется как-то ранжировать. С целью ранжирования информации вводится понятие ключа. Например, в шахматных программах очередные ходы помечают с помощью числовых критериев оценки текущей позиции. Одним из способов представления ранжированной информации является бинарное дерево.
Бинарное дерево, по определению, - это иерархическая структура, схематично представляемая, например, так, как показано на рис. 43.
Рис.43
Бинарное дерево имеет узлы, в которые может входить одна ветвь, а выходить не более двух (бинарное). Верхняя вершина не имеет входа и называется корнем. Построить бинарное дерево можно следующим образом. Пусть имеется набор числовых оценок для некоторого ключа, например, такой: 35, 86, 49, 27, 31, 56, 62, 44, 29, 26, 33, 88. Примем, что вправо по ходу дерева, начиная от корня, значение ключа увеличивается, а влево - уменьшается.
Type
PDerevo=ADerevo Derevo=record
Key:word;
Info: string;
Left,Right: Pderevo; End; .
В заключение рассмотрим такую задачу. Пусть требуется построить очередь для данных, содержащихся в текстовом файле a. txt. Можно записать:
Program Prim; |
|
|
|
Type PStroka = ^Stroka.; |
|
|
|
Stroka = record; |
|
|
|
Info:string; |
|
|
|
Sled: PStroka; |
|
|
|
End; |
|
|
|
Var T:TextFile; |
|
|
|
P,Pbegin,Pend:PStroka; |
|
|
|
S: string; |
|
|
|
Begin |
|
|
|
New(Pbegin); |
|
|
|
Pend:=Pbegin; |
|
|
|
P:=Pbegin; |
|
|
|
AssignFile(T,'A.txt'); |
|
|
|
Reset(T); |
|
|
|
While not eof(T) do begin |
|
|
|
Readln(T,S); |
|
|
|
Pend^.Sled:=P; |
|
|
|
Pend:=P; |
|
|
|
New(P); |
|
|
|
End; |
|
|
|
Pend^.sled:=nil; |
|
|
|
Dispose (P); |
|
|
|
CloseFile(T); |
|
|
|
End. |
|
|
|
Здесь указатель pbegin является идентификатором начала очереди, поэтому ему еще до начала цикла выделяется память (New (Pbegin) ;). Указатель pend необходим для запоминания последнего элемента очереди. Вспомогательный указатель р позволяет обеспечить циклические вычисления. Цикл построен так, что указатель Pend постоянно продвигается по списку, обеспечивая добавление очередного элемента в конец очереди.
156
157
ПРИМЕР ПРИЛОЖЕНИЯ 19
Среда Delphi содержит встроенный класс TList, с помощью которого можно создавать однонаправленные списки. Этот класс предназначен для организации списка указателей на размещенные в адресном пространстве какие-либо структуры данных и обеспечивает эффективную работу с элементами списка. Доступ к отдельному элементу списка осуществляется посредством индексированного свойства ItemsfIndex]. Нумерация элементов начинается с нуля. Элементы списка можно добавлять в его конец с помощью метода Add или в середину, используя метод Insert. Можно реализовать сортировку элементов с помощью метода Sort. Однако, так как заранее состав структуры, на которую указывают указатели списка, неизвестен, необходимо методу Sort передавать адрес разрабатываемой программистом процедуры попарного сравнения каких-либо полей, входящих, в состав структуры отдельного элемента списка. Список создается посредством стандартного конструктора Create. Соответственно необходимо обеспечить его удаление в приложении с помощью деструктора Free.
Интерфейс и возможности приложения можно увидеть, изучая вариант решения данного примера (рис. 44). Окружности рисуют, используя обработчик ImagelMouseDown.
В примере 19 реализуется запись в список TList некоторой структуры данных, представляющей собой следующий тип (Туре), используемый для рисования окружностей:
pMyList = ^AList; AList = record
numP: Integer;
xP,yP:integer;
r:integer;
Col: TColor; end;.
Здесь numP - номер окружности; xp, yP - координаты центра окружности; r-радиус; Col - цвет.
Данное приложение состоит из трех модулей: основная форма, вспомогательная форма для ввода цвета рисования и третья форма, служащая для поиска и рисования заданного элемента. Ниже приводится текст программы основного модуля.
unit Unit1ist; interface
uses Windows, Messages, SysUtils, Variants, Classes,Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Buttons,UnitSearch; type
TForm1 = class (TForm) Edit1: TEdit; Label1: TLabel; Label2: TLabel; Imagel: Tlmage; Button1: TButton; Bevel1: TBevel; Button2: TButton;
Button3: TButton;
BitBtn1: TBitBtn;
Bevel2: TBevel;
Button4:TButton;
ProcedureFormCreate(Sender:TObject)
procedureFormClose(Sender:TObject;var Action: TCloseAction);
procedure Edit1KeyPress{Sender: TObject; var Key: Char);
procedure ImagelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click (Sender: TObject) ;
procedure Button4Click(Sender: TObject);
public
158
159
MyList: TList;
end;
PMyList = ^AList;
AList = record
numP: Integer; xP,yP:integer; r:integer; Col: TColor; end; var Form1: TForm1;
ARecord: PMyList; kZap:integer=0; implementation uses UnitColor; {$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject); begin
MyList := TList.Create;
Imagel.Canvas.Pen.Width := 2;
end;
procedure TForm1.PormClose(Sender:TObject;
var Action: TCloseAction);
begin
Dispose(ARecord);
MyList.Free; end;
procedure TForm1.Edit1KeyPress(Sender: TObject;
var Key: Char); begin
if not {key in ['O'..'9',#8]) thenkey:=#0; end;
procedure TForm1.ImagelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin
New{ARecord);
ARecord^.xP := x;
ARecord^.yP := y;
if length{Edit1.Text)=0 then Edit1.Text:='0';
ARecord^.R := StrToInt(Edit1.Text);
inc(kZap);
ARecord^.numP:=kZap;
Form2.ShowModal;
ARecordA.Col:=Form2.NewColor;
MyList.Add (ARecord) ;
with Imagel.Canvas, ARecord* do begin
Pen.Color := ARecord^.Col;
Ellipse (xP - R, yP - R, xP + R, ур + R);
TextOut(xP-Font.Size div 2,
160
yP+Font.Height div 2, IntToStr(numP));
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Imagel.Canvas.Brush.Color:=clWhite; Imagel.Canvas.Rectangle(0,0, Imagel.width,Imagel.Height);
end;
function CompareX(Iteml, Item2: Pointer): Integer;
begin
Result :=PMyList(Iteml)^.xP-PMyList(Item2)^.xP;
end;
procedure TForm1.Button2Click(Sender: TObject);
var i:integer; begin
MyList.Sort(OCompareX);
for i := 0 to (MyList.Count - 1) do begin ARecord:=MyList.Items[i]; ARecord^.numP:=i+l; end; end;
procedure TForm1.Button3Click(Sender: TObject);
var i:integer; begin
for i. := 0 to (MyList .Count - 1) do begin ARecord := MyList.Items[i]; with imagel.Canvas, ARecord^ do begin Pen.Color := Col;
Ellipse (xP - R, yP - R, xP + R, yP + R); TextOut(xP-Font.Size div 2,
yP+Font.Height div 2, IntToStr(numP));
end;
end;
end;
procedure TForm1.Button4Click(Sender: TObject); begin
Form3.Show; end; end.
В основном модуле строится список, который может быть отсортирован по координате хР. Все элементы отсортированного списка вновь нумеруются в соответствии с увеличением значений координаты хР. Далее приводится программный код второго модуля. В этом модуле используется новый компонент TColorBox, который обеспечивает ввод требуемого цвета.
unit UnitColor;
161
interface
uses Windows, Messages, SysUtils, Variants, Classes,Graphics,
Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls; type
TForm2 = class(TForm) ColorBox1: TColorBox; Label1: TLabel; procedure ColorBox1Change(Sender: TObject);
public
NewColor:TColor;
end;
var Form2: TForm2; implementation
($R *.dfm}
procedure TForm2.ColorBox1Change(Sender: TObject); begin
NewColor:=ColorBox1.Selected;
close; end; end.
Интерфейс второй формы представлен на рис. 45. После выбора цвета осуществляется автоматический возврат в основную форму.
Рис. 45
Программный код третьего модуля приводится ниже.
unit UnitSearch;
interface
uses Windows, Messages, SysUtils, Variants, Classes,Graphics,
Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls; type
TForm3 = class(TForm) Label1: TLabel; Edit1: TEdit;
162
BitBtn1: TBitBtn; Button1: TButton; Bevell: TBevel; Bevel2: TBevel;
procedure Edit1KeyPress(Sender: TObject; var Key: Char) procedure Button1Clidt(Sender: TObject); end;
var Form3: TForm3; implementation uses Unit1ist, UnitColor;
{$R *.dfm} procedure TForm3.Edit1KeyPress(Sender: TObject;
var Key: Char);
begin
if length(Edit1.Text)*0 then Edit1.Text:='0'; if not (key in ['0'..'9',#8]) then key:=#0;
end;
procedure TForm3.Button1Click(Sender: TObject);
var k,i:integer; begin
if length(Edit1.Text)=O then Edit1.Text:='0'; k:=StrToInt{Edit1.Text);
if (k = 0) or (k > kZap) then begin
ShowMessage(
'Неверно задан номер точки');
exit; end;
for i := 0 to (Form1.MyList.Count - 1) do begin
ARecord := Form1.MyList.Items[i];
if ARecord^.numP=k then begin
with Canvas, ARecord^ do begin Pen.Color := Col;
Ellipse (xP - R, yP - R, xP + R, yP + R); TextOut(xP~Font.Size div 2,
yP+Font.Height div 2, IntToStr(numP)); end; break;
end;
end;
end; end.
В данном модуле предусмотрено рисование окружности не на элементе bnage, а непосредственно на канве формы, которая представлена на рис. 46. Модуль UnitSearch позволяет организовать поиск заданного элемента в сформированном связанном списке.
В данном проекте необходимо обратить внимание, как происходит работа, если приложение состоит из нескольких форм. Обычно имеются главная
163
и вспомогательные формы. Вызов других форм организуется с помощью кнопок или меню посредством метода Show (активная форма) или showHo-dal (неактивная форма).
Рис. 46 1
В программе выше используются следующие вызовы других форм: Form2.ShowModal, Form3.Show.
ПРОЦЕДУРНЫЙ ТИП
Выше рассматривались указатели, которые позволяли работать с переменными. Однако есть указатели, обеспечивающие работу с процедурами и функциями. Такие указатели представляют собой ссылки на адреса расположения подпрограмм и являются переменными процедурного типа. Объявляется процедурный тип следующим образом:
Туре Р1:Procedure; Р2;Procedure{var X,Y:real);
РЗ:Function(X:integer):double; Var Q2:P2; Q3:P3;.
Далее в программе может быть объявлено несколько функций с одним параметром, как указано в РЗ, например: Function Sum(X: integer) : double;, Funcnion Mult (X:integer) :double;. Далее можно записать следующие операторы: Q3:= sum; Y: = Q3(8); Q3:= Mult; Y:= Q3 (Z) ; Q2:= nil;.
В качестве значения переменной процедурного типа не могут использоваться стандартные подпрограммы из модуля System.
Процедурный тип используется очень широко при описании событий в классах, в которых он представляет указатели на методы.
ПРОГРАММНЫЕ ЕДИНИЦЫ DLL
DLL означает Dynamic Link Library {динамически компонуемая библиотека). DLL - это библиотеки, подключаемые к другим программным единицам во время их выполнения. Удобство их состоит в том, что ресурсы библиотек могут использоваться одновременно несколькими программами. Применение DLL не зависит от языка, на котором она написана. DLL составляют основу архитектуры Windows. Модель программирования на основе DLL позволяет добиться модульности всех элементов системы, а модульность операционной системы - предоставить программисту более динамичный и открытый интерфейс.
DLL формируются и компилируются независимо от использующих их программ, в которых будут только обращения к подпрограммам библиотек. В отличие от модуля Unit, DLL может передавать другим программным единицам только подпрограммы, хотя сама может иметь практически те же элементы, что и, например, программа-проект.
Заголовок DLL состоит из зарезервированного слова Library и имени библиотеки. Имя библиотеки совпадает с именем файла, в который записывается ее программный код. Раздел экспортирования начинается с зарезервированного слова Exports. Секция инициализации начинается с ключевого слова begin, а заканчивается end с точкой. Назначение ее такое же, как у аналогичной секции модуля Unit. Эта секция может отсутствовать. Остальные разделы могут присутствовать в любом количестве, как и в других программных единицах.
При создании DLL необходимо в репозитории (см. рис. 6) выбрать соответствующий пункт. Правила написания этой программной единицы такие же, что и у программы-проекта.
Отдельные части библиотеки можно поместить в модули. Основное отличие заключается в разделе экспортирования. Только подпрограммы, объявленные в этом разделе, могут быть использованы другими программными единицами.
Экспортируемые подпрограммы должны иметь внутреннее (в DLL) имя и могут иметь необязательное внешнее имя, которое используется как альтернатива при импортировании, например Exports Subroutinel name AddC, Subroutine2;.
164
165
ПРИМЕР ПРИЛОЖЕНИЯ 20
Пусть требуется построить библиотеку подпрограмм работы с комплексными числами и выполнить, например, сложение двух комплексных чисел, используя эту библиотеку. Пусть в библиотеке будет содержаться пока одна функция сложения двух чисел.
Так как необходимо ввести новый тип, декларация которого требуется в двух программных единицах (основной модуль, библиотека), создадим дополнительный модуль DeclComplex, содержащий только одно объявление типа. Данный пример дополним еще одной формой, в которой представим сведения о программе (рис. 47). Основная форма для этого примера представлена на рис. 48.
Рис. 48
При разработке данного приложения вначале необходимо построить библиотеку. Затем создать проект с формой рис. 48. Далее последовательно добавить в этот проект модуль с формой, представленной на рис. 47, и мо-
дуль с объявлением типа Complex. Ниже приводятся тексты кодов используемых программных единиц.
unit prim20; {Модуль с основной формой.}
interface
uses Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, Buttons,
ExtCtrls, Menus, DeclCoroplex, About;
type
Рис.
47
Edit6: TEdit;
Edit3: TEdit;
Edit4: TEdit; Label3: TLabel; Label5: TLabel; Label6: TLabel; Label7: TLabel; Label8: TLabel; Label9: TLabel; Label1O: TLabel; Label11: TLabel; MainMenu1: TMainMenu; Calc: TMenuItem; About: TMenuItem; ExitPoint: TMenuItem; Input: TMenuItem;
procedure Edit1KeyPress(Sender: TObject; var Key: Char); procedure CalcClick(Sender: TObject);
procedure ExitPointClick(Sender: TObject);
procedure InputClick(Sender: TObject);
procedure AboutClick(Sender: TObject);
end;
var MainForm: TMainForm;
Function AddC(X,Y:Complex) :Complx; external 'MyDLL.dll’;
implementation
{$R *.DFM}
procedure TMainForm.Edit1KeyPress(Sender: TObject;
var Key: Char);
begin
if not (key in ['0’..'9',’-',#8]) thenkey:=#0;
167
end;
procedure TMainForm.CalcClick(Sender: TObject);
var X,Y,Z:Complex; begin
X.Re:=StrToInt(Edit1.Text);
X.lm:=StrToInt(Edit2.Text);
Y.Re:=StrToInt(Edit3.Text);
Y.im:=StrToInt(Edit4.Text);
Z:=AddC(X,Y);
Edits.Text := FormatFloat('0.00',Z.Re);
Edit6.Text := FormatFloat('0.00',Z.Im); end;
procedure TMainForm.ExitPointClick(Sender: TObject); begin
Close; end;
procedure TMainForm.InputClick(Sender: TObject); begin
Edit1.SetFocus; end;
procedure TMainForm.AboutClick(Sender: TObject); begin
AboutForm.ShowModal;end; end.
{Модуль с дополнительной формой.}
unit About; interface uses
Windows, Classes, Graphics, Forms, Controls, StdCtrls, Buttons, ExtCtrls; type
TAboutForm = class(TForm) Panel1: TPanel; OKButton: TBitBtn; Programlcon: Tlmage; ProductName: TLabel; Department: TLabel; Copyright: TLabel; Comments: TLabel; end;
var AboutPorm: TAboutForm; implementation {$R *.DFM} end.
unit DeclComplex; {Модуль с объявлением типа Complex.}
interface
Type
Complex=record
168
Re,im:Real;
end;
implementation end.
Library MyDLL; {Программа DLL.)
uses SysUtils, Classes, DeclComplex; function AddC(X,Y:complex):complex; begin
with result do begin Re:=x.Re+Y.Re;
end; end;
Exports AddC; begin end.
Импортирование подпрограмм другими программными единицами возможно в статическом (неявном) или динамическом (явном) режимах. Если используется статическое импортирование подпрограммы, то задается заголовок такой подпрограммы с командой External. Если DLL является Windows-библиотекой или, например, написанной на C++, то необходимо указать соглашения о вызовах. Например, Procedure Procl (var x: Variant) ; Cdecl; External 'CDLL.dll';. В этом примере из библиотеки cdll . dll, написанной на C++, указано соглашение этого языка о передаче данных Cdecl.
При статическом импортировании сама библиотека вызывается операционной системой при первом к ней обращении и выгружается, когда потребность в ней отпадает.
При динамическом импортировании библиотека грузится непосредственно внутри той программной единицы, которой она необходима. После того как надобность в библиотеке исчезнет, ее нужно выгрузить. Для загрузки используется стандартная функция LoadLibrary (API-функция) с единственным параметром типа PChar, задающим имя библиотеки, например var Handle:HModule; . . . Handle: =LoadLibrary ('MyDll.dll') ;. Для выгрузки DLL используется подпрограмма FreeLibrary (Handle) ;.
Для работы с библиотекой необходимо получить адрес требуемой функции (или процедуры). При этом используется API-функция GetProcAd-ress(Handle: HModule, lpProcName: LPCSTR): FARPROC, которая ищет подпрограмму, указанную именем lpProcName. В качестве результата возвращается указатель на функцию (или процедуру) или nil. Полученный указатель должен быть далее приведен к нужному типу. Например, возможен такой вариант:
Onit uCallDll; Interface
169
type TAddC = Function(X,Y:Complex):Complex; TForm1=class(TForm)
…………………
Private {Добавим в описание формы}
DLLHandle: HModule; ExternalMyFunctionPointer:TFarProc; ExternalMyFunction:TAddC;
……………………………………………
Implementation
……………………………………
ProcedureTForm1.Button1Click(Sender:TObject);
………………………………
DLLHandle:=LoadLibrary('MyDll.dll'); ExternalMyFunctionPointer:= GetProcAdress(DLLHandle,
PChar('AddC'));
ExternalMyFunction:=TAddC(ExternalMyFunctionPointer)
В данном примере вполне работоспособном опущены некоторые операторы, с помощью которых проверяется, удачно или нет выполнено то или иное действие по получению ресурсов.
ТЕХНОЛОГИИ ПРОГРАММИРОВАНИЯ
Ниже будут рассмотрены некоторые стандартные для Windows технологии программирования. Вообще технология представляет собой набор методов и способов, позволяющих получить требуемые результаты. Поэтому знание различных подходов в программировании позволит разрабатывать профессионально законченные и надежно работающие приложения. Выше были рассмотрены технологии, например, построения циклических вычислительных процессов, построения динамически компонуемых библиотек и т.д. Далее рассмотрим более сложные технологии.