Rekordy, metody(constructor, destructor) ?

0

Witam!

Niestety jakiś czas nie działa mi pomoc do Turbo Delphi i nie moge z niej skożystać. Poszukiwałem także na sieci ale bez większego powodzenia.

Problem:
Czy można zdeklarowac konstruktor i destruktor dla rekordu. Dla object i class nie ma problemu. Znalazłem że można użyć następującej konstrukcji class constructor Create ... ale mój kompilator twierdzi że tak nie można. Interesuje mnie to z tego powodu iż dla rekordów mogę przeładować operatory a dla klas nie zabardzo(Win32). Jeżeli macie jakieś informacje na ten temat to proszę o ich udostępnienie na forum.

PS. Bardziej interesuje mnie destruktor aniżeli konstruktor poniewarz operatory przypisane do rekordu podczas działania zmieniają długośćdynamicznej tablicy będącej jednym z pól rekordu, i przy kończeniu danego podprogramu gdy rekord znika z pamięci zaalokowana pamięć na tablice nie zostaje zwalniana.

Gdyby istniała taka możliwość rzczony rekord wraz z operatorami zystał by funkcjonalność zbliżoną do systemowego String'a, a niestety bez tego mechanizmu, tracimy na wygodzie, a przy dłuższym działaniu programu możliwe będzie zassanie sporej iności pamięci(oczywiście zakładając że nie będzie się jej zwalniać ręcznie)

0

Wydaje mi się że w wypadku rekordów konieczne jest samodzielne zwalnianie po nich pamięci... Ale głowy nie dam. ;)

0

Nie ma możliwości zdefiniowania konstruktora/destruktora dla rekordu. Kończy się to błędem: [Pascal Error] E2465 A record cannot introduce a destructor

Ewentualnie strukturę tą możesz opakować jako pole pewnej klasy, ale lekko utraci to swoją 'przezroczystość' użycia.

0

Właśnie że rekord zwalania się sam tylko, że ja mam taki rekord

Jakiś_Rekord = record
  ...
  Data: array of Integer;
end;

i po wykonaniu podprogramu gdy wszystkie zmienne danego typu są usuwane ze stosu pozostaje niezwolniona pamięć po Data jeżeli nie było nil;

Było by bardzo fanie bdyby delphi pozwalało budować takie typy jak systemowy String, który w funkcjonalności defakto jest znacznie bardziej skomplikowany niż rekord z przeciążaniem operatorów(no ale cóż). Hm pomimo faktu zprowadzenia przeciązania operatorów, borland w żadnym z plików w dystrybucji Turbo Delphi ne używa operatorów

0

Ok znalazłem pod adresem http://delphi.about.com/od/adptips2006/qt/newdelphirecord.htm i rzczywiście jest taka możliwość ale od Delphi 2006 :( a szkoda

0
dj napisał(a)

Ok znalazłem pod adresem http://delphi.about.com/od/adptips2006/qt/newdelphirecord.htm i rzczywiście jest taka możliwość ale od Delphi 2006 :( a szkoda

Ciekawe.. :) A dlaczego szkoda? Turbo Delphi to właśnie D2k6, tylko TROSZKĘ okrojone. Więc powinno działać.

0

O ile mi wiadomo tablice dynamiczne nie są umieszczane na stosie (a jedynie wskaźnik do nich) i zarządzane są automatycznie (cokolwiek to znaczy), podobnie jak długie stringi

0

Proponuję przeanalizować przykład Demos\DelphiWin32\VCLWin32\ComplexNumbers

type
  TComplex = packed record{$IFDEF CIL}(IComparable, IConvertible){$ENDIF}
  strict private
  // No record class constructors in Win32 (yet)
{$IFDEF CIL}    class constructor Create; {$ENDIF}
    procedure Defuzz;
  private
    class procedure Init; static;
  public
    var
      Real, Imaginary: Double;
    class var
      Symbol: string;                 // defaults to 'i'
      SymbolBeforeImaginary: Boolean; // defaults to false
      DefuzzAtZero: Boolean;          // defaults to true

    class function From(const AReal: Double): TComplex; overload; static;
    class function From(const AReal, AImaginary: Double): TComplex; overload; static;
{$IFDEF CIL}
    class function FromObject(AObject: TObject): TComplex; static;
{$ENDIF}

    class function FromPolar(const AModulus, APhase: Double): TComplex; static;
    function Modulus: Double;
    function Phase: Double;
// (...)
0

Ok dzięki Oleksy_Adam za czynk o tym TComplex

dzięki niemu mam warzną wskazówkę

// No record class constructors in Win32 (yet)
{$IFDEF CIL} class constructor Create; {$ENDIF}

ale przebadałem sprawe dokładniej bo program nadal pokazywał przecieki pamięci

aktualnie mam coś takiego :

...
var
  a, b, c, d, s, g: BigInteger;

begin
  System.ReportMemoryLeaksOnShutdown:= True;

  a:= 1;
  b:= 2;
  s:= 40550;
  g:= -s;
  g:= g * 50;
  dec(g);

  Writeln(IntToStr(LongValue(s)));

  _BigIntClr(a);
  _BigIntClr(b);
  _BigIntClr(c);
  _BigIntClr(d);
  _BigIntClr(s);
  _BigIntClr(g);

  Writeln('  Alokowano pamiec: ', BigInts.me_alloc);
  Writeln('ReAlokowano pamiec: ', BigInts.re_alloc);
  Writeln('DeAlokowano pamiec: ', BigInts.de_alloc);

  Readln;
end.

Dodałem liczniki które każdorazowo liczą ilości alokowania, realokowania, dealokowania pamięci na dynamiczną tablice w BigInteger
i po tym całym procesie me_alloc = de_alloc a jednak dostaje raport o przeciekach, jak myślicie co może powodować takie zachowanie.

Ciekawostka po zakomentowaniu _BigIntClr(...); liczba de_alloc zmniejsza się ale wskazania przecieków pozostają takie same.

Zrobiłem także eksperymęt i za każdym razem alokowałem o 10 elementów więcej w tablicy i w tym momęcie wskazania przecieków zwiększyły się aczkolwiej nie zwiekszyła sie ich ilość

0

Witam
Chyba dobrze byłoby gdybyś pokazał więcej kodu. W szczególności co się dzieje podczas przypisania ":=" i działań, ktore są w tym przykładzie.
Swoją drogą to nie sądzicie, że to dziwne zachowanie borlanda ze wspieraniem recordów i to w tak połowiczny sposób? Jest konstruktor nie ma destruktora, konstruktor jest ale nie niezbędny, operatory można przeciążać tu a w klasach nie. O co tu chodzi? :)
BTW co to za moduł system, w ktorym jest coś takiego jak ReportMemoryLeaksOnShutdown?

0

ReportMemoryLeaksOnShutdown to zmienna typu Boolean w systemowy module dodawanym do każdego kompilowanego programu. Ustawienie tego na true powoduje że po zakończeniu aplikacji Memory Manager powoduje wypisanie niezwolnionej pamięci.

Hm: trochę kodu prosze bardzo:

 BigInteger = record
  private
    Ref: Integer;
    Sign: Integer; // wartości dodatani lub ujemna
    Data: array of Cardinal;
  public
    class operator Implicit(Value: Int64): BigInteger;
    class operator Implicit(Value: BigInteger): BigInteger;
    class operator Implicit(Value: String): BigInteger;
    ...
  end;


class operator BigInteger.Implicit(Value: Int64): BigInteger;
begin
  NewBigInteger(Result, Value);
end;

_________________________________________________

procedure _FreeBigInt(var a: BigInteger);
begin
  if a.Data <> nil then begin
     a.Data:= nil; // Zwalanianie
     Inc(de_alloc);
  end;
  a.Ref:= 0;
end;

procedure _BigIntClr(var S);
var
  a: BigInteger;
begin
  if Pointer(S) <> nil then begin
     a:= BigInteger(S);
     Pointer(S):= nil;
     if a.Ref > 0 then
        if InterlockedDecrement(a.Ref) = 0 then begin
           if a.Data <> nil then begin
              a.Data:= nil; // Zwalnianie
              inc(de_alloc);
           end;
        end;
  end;
end;


procedure NewBILen(var a: BigInteger; Length: Longint);
begin
  if Length <= 0 then
     Exit;
  _BigIntClr(a);

  if a.Data = nil then
     inc(me_alloc)
  else
     inc(re_alloc);

  SetLength(a.Data, Length);
  FillChar(a.Data[0], Length, 0);
  a.Sign:= 0;
  a.Ref:= 1;
end;

procedure NewBigInteger(var a: BigInteger; i: Int64);
begin
  _FreeBigInt(a);
  if Int64Rec(i).Hi = 0 then begin
     NewBILen(a, 1);
     a.Data[0]:= Int64Rec(i).Lo;
     a.Sign:= 1;
  end else begin
     NewBILen(a, 2);
     a.Data[0]:= Int64Rec(i).Hi;
     a.Data[1]:= Int64Rec(i).Lo;
     a.Sign:= 2;
  end;
  if i < 0 then
     a.Sign:= -a.Sign;
end;

Z tego wynika że definicja Implicit nie odpowiada za pewne rzeczy
i rzeczywiście tak jest po wejrzeniu w CPU(disasembler) widać że po wykonaniu kodu operatora wywoływany jest
call @CopyRecord
czyli systemowa funkcja odpowiadająca za kopiowanie rekordu.

Ciekawostka:
jeżeli wywołujemy s:= s1; i obie zmienne są typu String
wywoływana jest procedure _LStrAsg(var dest; const source); co daje znacznie wieksze możliwości niż operatory Implict w takiej postaci jakie są teraz.

0

Zmien funkcje _BigIntClr tak ja ponizej i dodaj typ wskaznikowy do rekordy BigInteger.

procedure _BigIntClr(var S);
var a: pBigInteger;
begin
  if @S <> nil then
  begin
     a := @s;
     if a.Ref > 0 then
        if InterlockedDecrement(a.Ref) = 0 then
        begin
           if a.Data <> nil then
           begin
              a.Data:= nil; // Zwalnianie
              inc(de_alloc);
           end;
        end;
  end;
end;

To powinno dzialac bo jak sam powiedziales ze gdy przypisujesz jeden rekord do drugiego to zostaje wywolana funkcja CopyRecord takze za kazdym razem gdy chciales uwolnic pamiec z rekordu to najpier powielales go i po tym uwalniales powielony rekord nie ten z parametru. :).

procedure _BigIntClr(var S);
var
  a: BigInteger;
begin
  if Pointer(S) <> nil then begin
     a:= BigInteger(S); // tutaj rekord zostaje powielony a powinien to byc adres pamieci do niego
     Pointer(S):= nil; //to linie wtedy trzeba usunac 
     if a.Ref > 0 then
        if InterlockedDecrement(a.Ref) = 0 then begin
           if a.Data <> nil then begin
              a.Data:= nil; // Zwalnianie
              inc(de_alloc);
           end;
        end;
  end;
end;
0

No jak ja to mgłem przegapić :) Wielkie dzięki sasio :)

0

NIE MA sprawy po to tu wszyscy jestesmy :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1