Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП / ООП_Лекции.doc
Скачиваний:
50
Добавлен:
08.06.2015
Размер:
1.03 Mб
Скачать

Примечание

Почему Object Pascal не поддерживает арифметические операции над указателями, хотя C/C++ это умеет? Потому, что арифметические операции над указателями - это наиболее частый источник ошибок. Вместо этого Object Pascal использует информацию о типе данных указателя, что позволяет спокойно применять к указателям процедуры инкремента Inc и декремента Dec.

LongInt = 1;

PChar; Char;

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

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

procedure TForml.Button2Click(Sender: TObject);

const ndx:

var pch: tmpC:

begin

pch := MemStrl.Memory;

tmpC := pch[ndx];

pch[ndx] := #0;

ListBoxl.Items.SetText(MemStrl.Memory);

pch[ndx] := tmpC;

if ndx < MemStrl.Size then

Inc(ndx)

else

Button2.Enabled := False;

end;

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

Обратите внимание, как используется локальная константа для хранения значения в памяти между различными обращениями к этому методу без добавления локального поля в форму. Рис. 3.3 показывает результат загрузки файла в Memo и Последующего просмотра данных потока памяти, добавляя их в список, размещенный внизу.

Написание заказного класса потока

Помимо использования уже существующих классов потока, программисты Delphi могут писать свои собственные классы потоков и использовать их вместо существующих. Чтобы сделать это, необходимо только определить, как обобщенный блок данных будет сохранен и загружен, a VCL будет способна использовать новый класс везде, где это требуется. Вам можно не создавать полностью новый класс потока для работы с новым типом средств, но придется настроить существующий поток. В этом случае требуется только переписать соответствующие методы чтения и записи.

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

type

TEncodedStream = class(TFileStream)

private

FKey: Char;

public

constructor Create(const FileName: string; Mode: Word);

function Read(var Buffer; Count: Longint):Longint; override;

function Write(const Buffer; Count: Longint):Longint; override;

property Key: Char read FKey write FKey default 'A'; e

nd;

Значение ключа просто добавляется к каждому из байтов при сохранении в файле и вычитается при чтении данных. Вот полный код двух методов:

constructor TEncodedStream.Create(const FileName: string; Node: Word);

begin

inherited Create(FileName, Mode); FKey := 'A';

end;

function TEncodedStream.Write(const Buffer;Count: Longint): Longint;

var

pBuf, pEnc: PChar; I, EncVal: Integer;

begin

// выделение памяти для декодированного буфера

GetNem (pEnc, Count);

try

// использование буфера в качестве массива символов

pBuf := PChar(@Suffer);

// для каждого символа буфера

for I := 0 to Count - 1 do

begin

// кодирование значения и его сохранение

EncVal := ( Ord(pBuf[I]) + Ord(Key)) mod 256;

pEnc[I] := Chr(EncVal);

end;

// запись закодированного буфера в файл

Result := inherited Write(рЕnc^, Count);

finally

FreeMem(pEnc, Count);

end;

end;

function TEncodedStream. Read(var Buffer;Count: Longint): Longint;

var

pBuf, pEnc: PChar; I, CountRead, EncVal: Integer;

begin

// выделение памяти под декодированный буфер

GetNem(pEnc, Count);

try

// чтение декодированного буфера из файла

CountRead := inherited Read(pEnc^, Count);

// использование буфера вывода в качестве строки

pBuf: PChar(@Buffer);

// для каждого прочитанного символа

for I := 0 to CountRead - 1 do

begin

// декодирование значения и его сохранение

EncVal := ( Ord(pEnc[I]) - Ord(Key)) mod 256;

pBuf[I] := Chr(EncVal);

end;

finally

FreeMem(pEnc, Count);

end;

// возвращается количество прочитанных символов

Result := CountRead;

end;

Комментарии в программе должны помочь понять детали. Теперь, когда у нас есть простой кодированный поток, можно попробовать использовать его в демонстрационной программе, названной EncDemo. Форма этой программы имеет два компонента Memo и три кнопки: первая кнопка загружает простой текстовый файл в первый компонент Memo; вторая кнопка сохраняет текст первого Memo в закодированном файле; и последняя кнопка перезагружает закодированный файл во второй Memo, декодируя его.

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

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

procedure TFormEncode.BtnSaveEncodedClick(Sender: TObject);

var

EncStr: TEncodedStream;

begin

if SaveDialogl.Execute then begin

EncStr := TEncodedStream.Create( SaveDialogl.FileName, fmCreate);

try

Memol.Lines.SaveToStream(EncStr);

finally

EncStr.Free;

end;

end;

end;

procedure TFormEncode.BtnLoadEncodedClick(Sender: TObject);

var

EncStr: TEncodedStream;

begin

if OpenDialogl.Execute then begin

EncStr := TEncodedStream.Create(OpenDialogl.FileName, fmOpenRead);

try

Memo2.Lines.LoadFromStream(EncStr);

finally

EncStr.Free;

end;

end;

end;

Соседние файлы в папке ООП