Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебник Емельянов.doc
Скачиваний:
12
Добавлен:
03.11.2018
Размер:
3.25 Mб
Скачать

Типы ссылки на класс

Такой программный элемент, как тип обычной переменной, представ­ляющий собой правила обращения с теми или иными переменными, сущест­вует только во время компиляции программы. Эти правила не могут изме­ниться во время выполнения программы. Типы ссылки на класс, которые синтаксически объявляются как class of TMyClass, позволяют нарушить указанные правила. С помощью этих типов можно обращаться к объектным типам во время выполнения программы. Для некоторого заданного типа

127

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

СВОЙСТВА

Свойства представляют интерфейс с внутренними полями данных того или иного объекта. Внутренние поля обычно объявляются в разделе Private. Их имена, как правило, начинаются с буквы F, например поле FColor. Соот­ветственно свойство, с помощью которого осуществляется доступ к задан­ному полю FColor, имеет имя Color. Свойства могут объявляться с различ­ной степенью доступа. Если требуется, чтобы они отображались в окне ин­спектора объектов, их объявляют в разделе Published.

Свойства могут создаваться с помощью различных типов данных, а именно:

  • Simple - простые свойства;

  • Enumerated - перечисляемые;

  • Set - множества;

  • Object - объектного типа;

  • Array - индексированные свойства.

Разные типы свойств по-разному отображаются в инспекторе объектов и имеют свои собственные редакторы для изменения значений свойств. Далее рассмотрим синтаксис записи различных видов свойств.

СВОЙСТВА SIMPLE

Простые свойства включают числовые, символьные и строковые типы данных. Наиболее часто используются свойства типа integer и string. Напри­мер, свойства Width, Height имеют тип integer, свойство Caption имеет тип string. Объявим для демонстрации синтаксиса записи простых свойств сле­дующий класс:

Туре

TSimple=class(TCustomControl) Private

PString: string; Published

Property StringProp:string read FString write FString; end;

Как следует из этого примера, свойства имеют тип поля, в котором хра­нится значение данного свойства. Кроме того, для свойства необходимо указать

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

СВОЙСТВА ENUMERATED

Свойства enumerated определяются типом перечень и логическим ти­пом Boolean. Обычно все возможные значения данного типа свойств некото­рым способом помечаются, например, как в объявлениях ниже:

туре

TEnumProp=(epZero, epOne, epTwo, epThree); TEnum=class(TCustomControl) Private

FEnumProp: TEnumProp; Published

Property EnumProp:TEnumProp read FEnumProp write FEnumProp; end;.

СВОЙСТВА SET

Элементы свойства Set (множество) в инспекторе объектов заключают­ся в квадратные скобки. Для развертывания в инспекторе объектов элемен­тов базового множества, включающего в себя все возможные варианты, имеется знак "+", расположенный слева от наименования свойства. Вклю­чить или не включить тот или иной элемент из базового множества в рабо­чее множество выбирают путем указания true или false. Рабочее множество определяет значение данного свойства. Рассмотрим пример объявления свойства Set. При этом используется тип TEnumProp, объявленный выше.

TSetProp=set of TEnumProp TSetClass=class(TCustomControl) Private

FSetProp:TSetProp; Published

Property SetProp:TsetProp read FSetProp write FSetProp; End;.

СВОЙСТВА OBJECT

Свойства Object в инспекторе объектов помечаются или знаком "+" или кнопкой с многоточием. Например, свойство Font имеет объектный тип. Дня того чтобы привести пример объявления свойства объектного типа, не­сводимо предварительно сформировать объект какого-либо класса и вы-

. 129

брать предка для этого класса. На практике в большинстве случаев в качест­ве предка выбирается встроенный класс TPersistent. Объявим класс, который будет определять далее в примере тип свойства.

Туре

T0bjectDop=class{TPersistent) Private

FMyInt:integer; Public

Property MyProp:integer read FMyInt write FMyInt; End;.

В данном случае нужно решить одну проблему. Объявленный выше класс (TObjectDop) должен войти как составная часть в класс, который должен будет содержать свойство заданного объектного типа TObjectDop. Соответственно потребуется задавать этому свойству значение, а это означа­ет, что потребуется объект типа TObjectDop, для которого необходимо обеспечить выделение памяти и освобождение памяти. Стандартных мето­дов выделения и освобождения памяти в данном случае недостаточно. Для выделения памяти создадим конструктор Create, а для высвобождения па­мяти - деструктор Destroy.

туре

TObjectProp=class(TCustomControl) Private

FObjectProp: TObjectDop; Public

Constructor Create(AOwner:TComponent);override; Destructor Destroy;override;

Published

Property ObjectProp: TObjectDop

read FObjectProp write FObjectProp; End;.

Конструктор и деструктор объявлены с командой Override. Это означа­ет, что в данном классе переопределяются имеющиеся по линии наследова­ния стандартные виртуальные конструктор Create и деструктор Destroy. Определим эти методы.

Constructor TObjectProp.Create(AOwner:TComponent); begin

Inherited Create(AOwner);

FObjectProp:= TObjectProp.Create;

end;

Destructor TObjectProp.Destroy; Begin

FObjectProp.Free;

130

Inherited Destroy; End;.

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

СВОЙСТВА ARRAY

Свойства Array позволяют создать очень похожие на массивы индекси­рованные свойства, которые отличаются от обычных массивов по двум ос­новным аспектам:

  • свойства типа Array могут индексироваться строковым значением;

  • свойства типа Array могут получать доступ только к одному элементу за одно чтение.

В окне Object Inspector эти свойства помечаются кнопкой с многоточием (…). Для изменения значений свойств данного вида вызывается специальный редактор. Ниже приводится пример объявления индексированного свойства.

Туре

Str7=string[7];

TMas=array [1..3] of str7; TMasClass=class(TCustomControl)

Private

FMasProp: TMas;

Function GetMasInt(pIndex:integer):string;

Function GetMasStr(pIndex:string):integer; Public

Constructor Create(AOwner:TComponent);override;

Property MasPropInt[Index:integer]:string read GetMasInt;

Property MasPropStr[Index:string]:4integer read GetMasStr; end;.

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

Constructor TMasClass.Create(AOwner:TComponent); begin

FMasProp[1]:='one';

FMasProp[2]:='two';

FMasProp[3]:='three'; End;

TMasClass.GetMasInt{pIndex:integer):string;

131

begin

result: = 'unknown' ;

if pIndex in [1..3] then result:= FMasProp[pIndex];

end;

Function TMasClass.GetMasStr(pIndex:string):integer;

var x:integer; begin

result:=-l; for x:=l to 3 do

if Uppercase(BMasProp[x])=Uppercase(pIndex) then begin

result:=x;

exit;

end;

end; .

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

ЗАДАНИЕ НАЧАЛЬНЫХ ЗНАЧЕНИЙ СВОЙСТВАМ

Когда создаются свойства, они первоначально получают нулевые или неопределенные (в зависимости от типа свойства) значения. Если необходи­мо задать свойствам какие-либо значения по умолчанию, то используются следующие шесть команд: Default, NoDefault, Default для свойств типа массив, Stored, index, Dispid. Например,

Property HumberProp:integer read Fnumber

write FNumber default 5; Property EnumProp: TEnumProp Read FenumProp

write FEnumProp default epOne;.

Для свойства типа массив команда Default указывается после точки с запятой без использования какого-либо значения. Массив, если он задан при объявлении класса, представляет собой начальные значения элементов ин­дексированного свойства.

Property ArrayPropInt[Index: integer] :

string read GetArrayPropInt; Default;.

Команда NoDefault применяется обычно для унаследованных свойств при указании, что родительское значение не действует.

Команда stored используется для того, чтобы указать, сохранять ли те­кущее значение, например,

Property NumProl:integer read Fnuml write FNuml Stored true; Property NumPro2:integer read FNum2 write FNum2 Stored false; Property NumPro3:integer read Fnum3 write Fnum3 Stored Fun3;-

132

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

Команда Index позволяет обеспечить гибкость при объявлении методов доступа Read и write к полям.

Рассмотрим это на примере. Пусть два свойства intProp1 и intProp2 для доступа к полям PIntProp1 и FIntProp2 используют одни и те же под­программы:

Property IntProp1:integer Index 1 read GetIntProp

write SetIntProp;

Property IntProp2:integer Index 2 read GetIntProp

write SetIntProp;.

Для указания того, что свойство IntProp1 работает с полем FIntProp1, a intProp2 - с полем FintProp2, используется команда index. В данном случае число после слова Index указывает, какое поле следует использовать, например, подпрограммы GetIntProp и SetIntProp можно определить так:

Function <имя класса>.GetIntProp (pIndex:integer):integer; begin

case pIndex of

1:result:=FIntProp1; 2:result:=FIntProp2; end; end; Procedure <иия класса>.SetIntProp (pIndex:integer; pValue:integer);

begin

case pIndex of

1: FIntProp1:= pValue;

2: FIntProp2:= pValue;

end; end;.

Команда Dispid применяется при работе с объектами OLE.

ПРИМЕР ПРИЛОЖЕНИЯ 16

Пусть требуется построить на форме три геометрические фигуры: круг, квадрат, эллипс. Необходимо обеспечить возможность изменения цвета ка­ждой фигуры. Пусть будет по два цветовых варианта каждой фигуры (рис. 36).

Для решения задачи построим четыре класса. Первый класс назовем TDrawArea. Включим в него следующие действия: инициализация всех по-

133

read GetBrushColor;

procedure DrawFig;virtual;abstract;

procedure Drawlt; end; TSquare = class(TDrawArea)

лей, рисование фигуры. Остальные три класса определяют конкретную реa лизацию рисования круга, квадрата и эллипса.

Рис. 36

Программа приводится ниже.

unit PRIM16;

interface

uses Windows, Messages, SysUtils, Classes, Graphics,

Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type

TForm1 = class(TForm) Panel1: TPanel; btnRedCircle: TButton; btnBlueCircle: TButton; btnBlackSquare: TButton; btnWhiteSquare: TButton; btnYellowEllipse: TButton; btnGreenEllipse: TButton; Button1: TButton;

procedure AllBtnClick(Sender: TObject); procedure Button1Click(Sender: TObject); end;

ArrayColor=array[1..6] of TColor; TDrawArea = class(TPersistent)

private

xl(yl,x2,y2:integer;

FArrayBrushColor: ArrayColor;

FPenColor: TColor;

function GetBrushColor(pindex:integer): TColor; public

constructor InitColor;

constructor InitXY(NewXl,NewYl,NewX2,NewY2rinteger);

property PPenColor: TColor read FpenColor

write FpenColor Default clBlack;

property PBrushColor[pindex:integer]: TColor

procedure DrawFig;override; end;

TEllipse=class{TDrawArea)

Procedure DrawFig;override; end;

TCircle = class(TDrawArea)

procedure DrawFig;override; end; var Form1: T Form1;

MySquare:TSquare; MyEllipse:TEllipse; MyCirde: TCircle ; implementation

{$R *.DFM}

Constructor TDrawArea

Begin InitColor

FArrayBrushColor[1] :=clRed;

FArrayBrushColor[2] :=clBlue;

FArrayBrushColor[3] :=clBlack;

FArrayBrushColor[4] :=clWhite;

FArrayBrushColor[5] :=clYellow;

FArrayBrushColor[6] :=clGreen;

End;

Constructor TDrawArea.InitXY(HewXl,NewYl,NewX2,NewY2:

integer);begin

xl:=NewXl;

yl:=NewYl;

x2:=NewX2;

y2:=NewY2; end; Function TDrawArea.GetBrushColor(pIndex:integer):TColor;

begin

result:=-1;

if pIndex in [1..6] then result:=FArrayBrushColor[pIndex]; end;

procedure TDrawArea.Drawlt; begin

DrawFig; end;

procedure TSquare.DrawFig; begin

Form1.Canvas.Rectangle(xl,yl,x2,y2);

134

135

end;

procedure TCircle.DrawFig;

begin

Form1.Canvas.Ellipse(xl-x2,yl-x2,xl+x2,yl+x2);

end;

procedure TEllipse.DrawFig; begin

Form1.Canvas.Ellipse(xl,yl,x2,y2);

end;

procedure TForm1.AllBtnClick(Sender: TObject);

begin

if (Sender as Tbutton=btnRedCircle) or

(Sender as Tbutton=btnBlueCircle) then begin

if not assigned(MyCircle) then begin MyCircle:=TCircle.Create;

MyCircle.InitColor;

MyCircle.InitXY(50,50,40,0); Form1.Canvas.Pen.Color:=MyCircle.PPenColor;

end;

if Sender as Tbutton = btnRedCircle then begin Form1.Canvas.Brush.Color:=MyCircle.PBrushColor[1]; MyCircle.Drawlt;

end;

if Sender as Tbutton = btnBlueCircle then begin Form1.Canvas.Brush.Color:=MyCircle.PBrushColor[2]; MyCircle.Drawlt;

end;

end;

if (Sender as Tbutton=btnBlackSquare) or

(Sender as Tbutton=btnWhiteSquare) then begin

if not assigned(MySquare) then begin MySquare:=TSquare.Create;

MySquare.InitColor;

MySquare.InitXY(110,10,190,90}; Form1.Canvas.Pen.Color:=MySquare.PPenColor;

end;

if Sender as Tbutton = btnBlackSquare then begin Form1.Canvas.Brush.Color:=MySquare.PBrushColor[3]; MySquare.Drawlt;

end;

if Sender as Tbutton = btnWhiteSquare then begin Form1.Canvas.Brush.Color:=MySquare.PBrushColor[4]; MySquare.Drawlt;

end;

end;

if (Sender as Tbutton=btnYellowEllipse) or

(Sender as Tbutton=btnGreenEllipse} than begin

if not assigned(MyEllipse) then begin

136

MyEllipse:=TEllipse.Create; MyEllipse.InitColor; MyEllipse.InitXY(220,10,280,90); Form1.Canvas.Pen.Color:=MyEllipse.PPenColor; end;

if Sender as Tbutton = btnYellowEllipse then begin Form1.Canvas.Brush.Color:=MyEllipse.PBrushColor[5]; MyEllipse.Drawlt; end;

if Sender as Tbutton = btnGreenEllipse then begin Form1.Canvas.Brush.Color:=MyEllipse.PBrushColor[6]; MyEllipse.Drawlt; end; end; end;

procedure TForm1.Button1Click(Sender: TObject); begin

if assigned(MyCircle) then MyCircle.Free; if assigned(MySquare) then MySquare.Free; if assigned(MyEllipse) then MyEllipse.Free; close; end; end.

В данном примере показано, как работать со свойствами типа Array. Следует обратить внимание, что каждый из трех используемых объектов должен строиться один раз. Для проверки существования объекта использу­ется функция Assigned. При выходе из программы (Button1ciick.) все объекты разрушаются. В данной программе показано, как использовать один обработчик событий (AllBtnClick) для всех шести кнопок построения гео­метрических фигур. Используемые конструкторы InitColor и InitXY соз­даны не для построения объектов, а для инициализации данных, поэтому в их описании отсутствует вызов inherited.

ФАЙЛОВЫЕ ТИПЫ

Тип файл представляет собой последовательность элементов одного ти­па, обычно расположенных на внешнем устройстве, например на жестком Диске. Можно выделить три основных типа файлов на физическом устройст­ве:

1. Данные в файле располагаются в виде строк разной длины. Каждая строка заканчивается двумя символами #13 #10 (#13 - возврат каретки,#10

137

перевод строки). Эти символы отделяют строки друг от друга. Максималь­ный размер строки ограничен, чаще всего он равен 128 символам, можно увеличить этот размер до максимального, равного 255 символам. При поиске нужной информации в таком файле необходимо найти #13 #10, прочитать найденную строку и проверить, есть ли заданная информация в этой строке, затем опять следует искать #13 и #10 и т.д. Таким образом, поиск может осуществляться, последовательно читая этот файл только сначала, т.е. файл рассматривается как один массив строк, разделенных символами #13 #10. Если нужно вставить информацию в середину такого файла, то это можно сделать путем переписывания старого файла в новый со вставкой новых строк и последующим удалением старого файла. Операционная система до­пускает дописывание информации в конец файла. В соответствии со свойст­вами такой файл называют файлом последовательного доступа, или тек­стовым.

  1. Данные представляются в виде записей одинаковой длины. Никаких разделителей между записями нет. Операционная система может располо­ жить головку считывания-записи информации на любой байт (установить файловый указатель). Так как все записи одинаковой длины, файловый ука­ затель операционной системой может быть установлен на начало любой за­ писи, таким образом получается, что все записи будто бы пронумерованы. Первая запись нумеруется числом 0. Если длина записи равна 80 байт, то установку файлового указателя на начало второй записи (запись с номером 1) операционная система выполняет на отметке 80 байт. Эту запись можно прочитать или перезаписать. Такие файлы являются типизированными - файлами прямого доступа (в отличие от текстовых файлов).

  2. Файлы рассматриваются как последовательность байт. Эти файлы яв­ ляются файлами прямого доступа с длиной записи, чаще всего, равной 1 байту. С помощью такого подхода можно прочитать любой файл, но трудно интерпретировать информацию.

При работе с файлами автоматически проверяются ошибки ввода или вывода, если включена (по умолчанию) директива {$I+}. В этом случае при возникновении ошибки программа завершается. Если директива проверки файловых ошибок отключена {$I-}, то программа продолжает выполняться и можно проверить результат работы с файлом с помощью стандартной функ­ции IOResult.

ТЕКСТОВЫЕ ФАЙЛЫ

Этот тип файлов объявляется с помощью файловой переменной, типа TextFile:Var т : TextFile;.

Перед тем как начинать работу с файлом, необходимо файловую пере­менную связать с конкретным файлом. Это осуществляется с помощью сле-

138

дующей процедуры: AssignFile((<ф.п.>, Name) ; , где <ф.п.>-файловая переменная, Name - строка типа String, содержащая имя файла, например AssignFile(T, ‘a.txt') ; .Строка Name может содержать не только имя файла, но и путь к файлу.

По окончании работы с файлом его нужно закрыть процедурой Close-File (<ф. п. >). Это требование обязательно должно соблюдаться для фай­лов, в которые записывалась информация. С помощью файлов можно вво­дить информацию в память, хранить информацию, передавать ее от одних программных единиц к другим. Для чтения и записи информации использу­ются встроенные стандартные процедуры.

Перед чтением информации необходимо явно указать, что файл будет использоваться только для чтения с помощью процедуры Reset (<ф.п.>) . Эта процедура устанавливает файловый указатель на начало файла. Чтение информации осуществляется с помощью следующих процедур:

ReadLn(<ф.п.>, <список>); Read(<ф.п.>, <список>);.

Первая процедура позволяет полностью читать строку вместе с симво­лами #13 #10. Вторая процедура читает элемент или несколько элементов строки, но в пределах одной строки. Например, Read(T, a,b,c) ; ReadLn(T, a,b,c);.

Если а, Ь, с - строковые переменные, то первый оператор заполнит дан­ными только одну переменную а (Ь и с останутся пустыми).

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

При чтении чисел автоматически происходит перекодирование инфор­мации в двоичный код. Если элементы не строковые, то в переделах одной строки файла может находиться несколько элементов, обязательно отделен­ных друг от друга разделителями. В качестве разделителей используются следующие символы: пробел, символ табуляции, возврат каретки (#13). При подготовке информации необходимо соблюдать эти требования - запи­сывать в явном виде разделители. Процедуры Read и ReadLn читают инфор­мацию последовательно по одному символу (в том числе читаются #13, #10 и #26 - конец файла), причем, если чтение происходит не в строковые пере­менные, то значимыми для Read являются все виды разделителей, а для ReadLn - только #13 и #10. Например, при чтении информации в строковые переменные String или PChar заносятся очередные символы до #13 и #10.

139

Для подготовки текстовых файлов можно использовать не только про­граммную среду Delphi, но и любые простые редакторы.

При записи информации необходимо для файла задать явно режим запи­си. Для текстовых файлов режим записи задается одновременно с режимом создания нового файла. При этом используется процедура Rewrite (<ф. п. >). Если файл, указанный в соответствующей процедуре AssignFile, существует, то он уничтожается и создается новый. Запись ин­формации в файл осуществляется процедурамиWrite(<ф.п. >,<список>) или WriteLn(<ф. п. >,< список >). Write записывает очередной элемент строки, WriteLn записывает строку вместе с #13 и #10. В отличие от проце­дур чтения, в списке можно указывать не только переменные, но и выраже­ния. Если элементы числовые, то в списке необходимо явно предусматри­вать разделители между этими элементами. Например, writeLn(T, 'число а=' ,а,' ' , 'число b=' ,b) ;.