4. Objekty a súbory
Domovská stránka Programovanie v C++ OOP a Windows Ročníkový projekt Princípy počítačov Princípy databáz Všeličo Kontakt

 

Na prednáške:


Stream (prúd)

Stream je lineárny súhrn údajov, ku ktorým zvyčajne pristupujem sekvenčne - čiže ich postupne, od začiatku do konca čítame alebo ukladáme. Pri práci so streamami využívame objektové triedy:
  TStream       
... základná trieda, ktorá má abstraktné metódy pre zápis, čítanie;
  TFileStream    ... odvodená trieda pre prácu so súbormi;
  TMemoryStream  ... stream v pamäti (zapisuje/číta údaje do/z pamäte).

Ukážka - zápis čísel do súboru pomocou TFileStream:

procedure Zapis;
var
 
S: TStream;
  I: Integer;
begin
  S:=TFileStream.Create('cisla.dat', fmCreate);
  try
    for
I:=1 to 15 do
      S.WriteBuffer(I, SizeOf(I));
  finally
    S.Free;
  end;
end;

Ukážka - čítanie čísel zo súboru pomocou TFileStream:

procedure Precitaj;
var
  S: TStream;
  I, Hod: Integer;
begin
  S:=TFileStream.Create('cisla.dat', fmOpenRead);
  try
    for
I:=1 to 15 do begin
     
S.ReadBuffer(Hod, SizeOf(Hod));
      Form1.Memo1.Lines.Add(IntToStr(Hod));
    end;
  finally
    S.Free;
  end;
end;

Používanie TMemoryStream je veľmi podobné akurát, že pri vytváraní objektu nemusíme dopredu určiť spôsob práce so streamom (netreba určovať, či ideme zapisovať alebo čítať údaje).


Ukladanie objektov do súboru

Majme deklarované:

  var
    H: TSkelet;
    S: TStream;
 
... tu nejako inicializujeme a vytvoríme kostru (TSkelet)

Zlé príklady ukladania objektov:

Čo by malo ukladanie a čítanie zabezpečovať:

Problém je v tom, že do súboru nemôžeme ukladať adresy objektov, pretože pri čítaní a konštruovaní môže byť pamäť inak obsadená a objekty už s veľkou pravdepodobnosťou nebudú vytvorené na rovnakých adresách. Preto musíme ukladanie atribútov objektu sami naprogramovať, pričom sa ale musíme vyhnúť ukladaniu smerníkov na iné objekty:

type
  TSkelet=class
  private
   
...
  protected
   
...
    procedure DoLoad(Stream: TStream); virtual;
    procedure DoSave(Stream: TStream); virtual;
  public
    constructor Create(Parent: TSkelet); virtual;
   
...
    class procedure RegisterClass(Id: Cardinal);
    class function GetClassFromId(Id: Cardinal): TSkeletClass;
    class function GetIdFromClass(ClassType: TClass): Cardinal;
    class function Load(Stream: TStream; Parent: TSkelet): TSkelet;
    procedure Save(Stream: TStream);

    ...
  end;

Potom zápis do súboru vyzerá nasledovne:
  S:=TFileStream.Create('skelet.dat', fmCreate);
  H.Save(S);
  S.Free;

A čítanie zo súboru:
  S:=TFileStream.Create('skelet.dat', fmOpenRead);
  H:=TSkelet.Load(S, nil);
  S.Free;

Implementácia:

var
  ClassList: array of record
    Id: Cardinal;
    ClassType: TSkeletClass;
  end;

class procedure TSkelet.RegisterClass(Id: Cardinal);
var
  Index: Integer;
begin
  Index:=Length(ClassList);
  SetLength(ClassList, Index+1);
  ClassList[Index].Id:=Id;
  ClassList[Index].ClassType:=Self;
end;

class function TSkelet.GetClassFromId(Id: Cardinal): TSkeletClass;
var
  Index: Integer;
begin
  for Index:=0 to Length(ClassList)-1 do
  if ClassList[Index].Id=Id then begin
    Result:=ClassList[Index].ClassType;
    Exit;
  end;
  Result:=nil;
end;

class function TSkelet.GetIdFromClass(ClassType: TClass): Cardinal;
var
  Index: Integer;
begin
  for Index:=0 to Length(ClassList)-1 do
    if ClassList[Index].ClassType=ClassType then begin
      Result:=ClassList[Index].Id;
      Exit;
    end;
  Result:=0;
end;

class function TSkelet.Load(Stream: TStream; Parent: TSkelet): TSkelet;
var
  ClassType: TSkeletClass;
  Id, Index, Count: Integer;
begin
  Stream.ReadBuffer(Id, SizeOf(Id));
  ClassType:=GetClassFromId(Id);
  if ClassType=nil then begin
    Result:=nil;
    Exit;
  end;
  Result:=ClassType.Create(Parent);
  try
    Result.DoLoad(Stream);
    Stream.ReadBuffer(Count, SizeOf(Count));
    for Index:=0 to Count-1 do Load(Stream, Result);
  except
    Result.Free;
    raise;
  end;
end;

procedure TSkelet.Save(Stream: TStream);
var
  Id, Index, Count: Integer;
begin
  Id:=GetIdFromClass(ClassType);
  if Id=0 then Exit;
  Stream.WriteBuffer(Id, SizeOf(Id));
  DoSave(Stream);
  Count:=ChildCount;
  Stream.WriteBuffer(Count, SizeOf(Count));
  for Index:=0 to Count-1 do Childs[Index].Save(Stream);
end;

procedure TSkelet.DoLoad(Stream: TStream);
begin
  Stream.ReadBuffer(FdX, SizeOf(FdX));
  Stream.ReadBuffer(FdY, SizeOf(FdY));
  Stream.ReadBuffer(FX, SizeOf(FX));
  Stream.ReadBuffer(FY, SizeOf(FY));
end;

procedure TSkelet.DoSave(Stream: TStream);
begin
  Stream.WriteBuffer(FdX, SizeOf(FdX));
  Stream.WriteBuffer(FdY, SizeOf(FdY));
  Stream.WriteBuffer(FX, SizeOf(FX));
  Stream.WriteBuffer(FY, SizeOf(FY));
end;

...

initialization
 
TSkelet.RegisterClass(1);
end.

V prípade, že sa v stromovej štruktúre nachádzajú objekty z inej objektovej triedy, musíme tieto triedy zaregistrovať ešte predtým, ako zavoláme metódy Load, alebo Save. Novú triedu registrujeme podobne, ako TSkelet, napríklad:

upravená trieda TSkelet

©  2003 Ľubomír SALANCI