настраиваемая управляемая запись и утечка памяти

#delphi #delphi-10.4-sydney

#delphi #delphi-10.4-Сидней

Вопрос:

Используя Delphi 10.4.1, я попробовал пользовательское управляемое управление записями для инициализации записи, но все равно получаю утечки памяти.

 unit Unit3;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, System.IOUtils, System.DateUtils, System.Character,
  Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TForm3 = class(TForm)
    Button1: TButton;
    RadioGroup1: TRadioGroup;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form3: TForm3;

type
  TMyRec = record
    DateTime: TDateTime;
    v, size: integer;
    str: string;
    class operator Initialize (out Dest: TMyRec);
  end;

  TMyREcHolder = class
    data: TMyRec;
    constructor Create(const e: TMyRec);
  end;

  TMyList = class(TList)
    procedure Clear; override;
  end;

implementation

{$R *.dfm}

class operator TmyRec.Initialize (out Dest: TMyRec);
begin
  Dest.str := '';
end;

{ TMyREcHolder }


constructor TMyREcHolder.Create(const e: TMyRec);
begin
  inherited Create;
  data := e;
end;

{ TMyList }

procedure TMyList.Clear;
var
  i: integer;
begin
  for i := 0 to Count - 1 do
    TMyREcHolder(Items[i]).Free;
  inherited Clear;
end;

procedure TForm3.Button1Click(Sender: TObject);
var
  lst: TMyList;
  i: integer;
  rec: TMyRec;
  FI: TSearchrec;
begin
  Initialize(rec);
  lst := TMyList.Create;
  try
    if FindFirst(TPath.Combine('C:temp', '*.txt'), faAnyFile, FI) = 0 then
    begin
      repeat
        if (FI.FindData.dwFileAttributes and faDirectory = 0) and
          (FI.FindData.dwFileAttributes and faArchive = faArchive) then
        begin
          Application.ProcessMessages;
          case RadioGroup1.ItemIndex of
            0: Initialize(rec);
            1: rec.str := '';
            2: fillchar(rec, sizeof(rec), 0);
          end;
          try
            rec.DateTime := FI.TimeStamp;
          except
            rec.DateTime := EncodeDateDay(1970, 1);
          end;
          rec.size := FI.size;
          rec.str := FI.Name;
          lst.Add(TMyREcHolder.Create(rec));
        end;
      until (FindNext(FI) <> 0);
      FindClose(FI);
    end;
  finally
    lst.Free;
  end;
end;

end.
 

Radiogroup предлагает три элемента, как 1, так и 3 утечки памяти. Кто-нибудь может объяснить, почему выполняется инициализация? Мне нужен надежный способ очистки записи, который помог бы мне отучить себя от 20-летней привычки к заполнению.

Комментарии:

1. Разве смысл Initialize оператора не в том, что компилятор вызывает его для вас при создании записи? Если вы хотите очистить запись в цикле, сделайте rec := Default(TMyRec) . Глядя на ваш код, я думаю, что вы неправильно поняли, что Initialize делает оператор. Похоже, вы считаете, что он может быть вызван вами, когда захотите, но, по крайней мере, насколько я понимаю, он вызывается компилятором, вводящим неявные вызовы, выполняемые при создании записей.

2. Да, я просто предположил, что могу использовать функцию записи, когда захочу. Странно, что я не могу ввести пользовательскую функцию, которую я могу просто вызвать (например, в классе). Я хотел использовать документированный оператор. Я понимаю, что Default существует уже давно, но не является официальным. Спасибо, что прояснили это.

3. Default() является официальным. Когда вы звоните Initialize(rec) , вы звоните не своему оператору. Вы вызываете функцию RTL. И тот, который IIRC, вы не должны вызывать. Поэтому я считаю, что ответ на ваш вопрос по-прежнему заключается в том, что вы должны использовать rec := Default(...) вместо своих FillChar вызовов.

4. Другой вопрос, чего вы пытаетесь достичь здесь? строки являются управляемым типом, и они будут автоматически инициализированы в записи. Далее, почему вы храните запись в объекте TMyREcHolder? Если вы уже используете классы, вы можете также поместить все поля в класс. Я бы использовал записи с общим списком TList<TMyRec> и избегал возни с управлением памятью.

5. Да, создайте и используйте понятную процедуру. Использование FillChar для очистки записи является и всегда было проблематичным, когда сохраняется какой-либо управляемый тип, потому что нет возможности выполнить управление (управление выполняется с помощью магии компилятора).