Работа с классами и объектами - утечка памяти + fastMM

Jude
Дата: 06.06.2011 22:04:56
Установил FastMM.
сделал тестовое приложение:

+
unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls,bisneslogik;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure ButonXclick(Sender:TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    desk:Tdesk;
    pole1:tpole;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var i,j:integer; btcur:TButton;
begin
  //create new field
  desk := tdesk.create;
  pole1:= tpole.create(77); 
  for i:=1 to 8 do
  for j:=1 to 8 do
  begin
    btcur := TButton.Create(panel1);
    btcur.Width:=50;
    btcur.Height := 50;
    btcur.Tag := i*8+j-8;
    btcur.name := 'Button'+inttostr(btcur.tag);
    btcur.Top := i*60-55;
    btcur.Left := j*60-55;
    if (i+j)mod 2 >0 then
    btcur.Brush.Color := clYellow else
    btcur.Brush.Color := clblack;

    btcur.OnClick := ButonXclick;
   { case desk.fpole[btcur.Tag].in_cell of
    BlackKaptain: btcur.Font.Color := clRed;
    BlackLeitenaunt: btcur.Font.Color := clRed - $111111;
    BlackSolder: btcur.Font.Color := clRed-$333333;
    WhiteSolder: btcur.Font.Color := clGreen;
    whiteLeitenaunt: btcur.Font.Color := clGreen- $111111;
    WhiteKaptain: btcur.Font.Color := clGreen-$333333;
    Empty : btcur.Font.Color := clBlue;
    end;}
    btcur.Parent := panel1;
  end;
end;


procedure TForm1.ButonXclick(Sender:TObject);
begin
  if sender is Tbutton then
  panel2.Caption := inttostr(Tbutton(Sender).Tag);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  desk.Free;
  pole1.Free;
end;

end.

+
unit bisneslogik;

interface
uses SysUtils;

type
TinCellType = (BlackKaptain,BlackLeitenaunt,BlackSolder,Empty,WhiteSolder,whiteLeitenaunt,WhiteKaptain);

Tpole= class
id:integer;
xcor,ycor:integer;
in_cell: TinCellType;
public
  constructor create(a:integer);
  //destructor destroy;
end;
Tdesk=class
fpole : array [1..64]of Tpole;
public
  constructor create;
  destructor destroy;
end;


implementation

uses Math;

constructor Tpole.create(a:integer);
begin
  inherited create;
  id:=a;
  xcor:=(a-1) mod 8 +1;
  ycor := (a-1) div 8 +1;
  in_cell := Empty;
end;

{destructor Tpole.destroy;
begin
  inherited;
end;
 }
constructor Tdesk.create;
var i,j:integer;
begin
  inherited create;
  for i:=1 to 8 do
  for j:=1 to 8 do
  begin
    fpole[i*8+j-8]:=Tpole.create(i*8+j-8);
    if i<3 then fpole[i*8+j-8].in_cell := BlackSolder;
    if i>6 then fpole[i*8+j-8].in_cell := WhiteSolder;
  end;
end;

destructor Tdesk.destroy;
var j:integer;
begin
  for j:=1 to 64 do FreeAndNil(fpole[j]);
  inherited;
end;


end.

при уничтожении объекта - закрытие сразу после запуска, получаю ругательство о утечке 20 байт 64 раза в Tpole.

for j:=1 to 64 do FreeAndNil(fpole[j]);
пробовал менять на
for j:=1 to 64 do fpole[j].Free;
картина та же.

после подстановки в классе Tdesk
  destructor destroy; override;
утечка пропадает.

Вопрос: в чем моя ошибка при работе с классом Tdesk?

мое понимание, что вместо перезагрузки метода делал его замену, и этим терял очистку указателей fpole.

так ли это?

с классами и ошибками при работе с ними пытаюсь щас разбираться. Если понял не верно, пожалуйста подправьте.

спасибо.
_Vasilisk_
Дата: 06.06.2011 22:17:03
Jude
мое понимание, что вместо перезагрузки метода делал его замену, и этим терял очистку указателей fpole.
так ли это?
Так и есть. Деструктор это виртуальный метод. Сравни результаты
TParent = class
  destructor Destroy; override;
end;

TChild1 = class(TParent)
  destructor Destroy; override;
end;

TChild2 = class(TParent)
  destructor Destroy; 
end;

destructor TParent.Destroy;
begin
  ShowMessage('TParent.Destroy');
  inherited Destroy;
end;

destructor TChild1.Destroy;
begin
  ShowMessage('TChild1.Destroy');
  inherited Destroy;
end;


destructor TChild2.Destroy;
begin
  ShowMessage('TChild2.Destroy');
  inherited Destroy;
end;

// ...............................

begin
  TChild1.Create.Free;
  TChild2.Create.Free;
  // Так никогда не делать
  TChild1.Create.Destroy;
  TChild2.Create.Destroy;
end;
ДжекНепотрошитель
Дата: 06.06.2011 22:22:47
Когда ты делаешь desk.Free, процедура Free вызывает деструктор Tdesk.Destroy. Так вот, у класса TDesk окажется два деструктора Destroy - стандартный, унаследованный от TObject, и твой деструктор, в котором ты очищаешь поле. И если ты не укажешь директиву override, Free будет вызывает стандартный. Который ничего не делает. Если же указать override, твой деструктор подменит вызовы стандартного, и будет вызываться вместо него.
Jude
Дата: 06.06.2011 22:31:40
_Vasilisk_,

спасибо. очень поучительный пример))). мне понравилось.
Jude
Дата: 06.06.2011 22:37:57
ДжекНепотрошитель,

благодарю. есть полная ясность.

вопрос можно считать закрытым.
Negruzzi Cristian
Дата: 07.06.2011 09:14:07
ДжекНепотрошитель
Когда ты делаешь desk.Free, процедура Free вызывает деструктор Tdesk.Destroy. Так вот, у класса TDesk окажется два деструктора Destroy - стандартный, унаследованный от TObject, и твой деструктор, в котором ты очищаешь поле. И если ты не укажешь директиву override, Free будет вызывает стандартный. Который ничего не делает. Если же указать override, твой деструктор подменит вызовы стандартного, и будет вызываться вместо него.

а если использовать reintroduce ?
Джурджулешты
Дата: 07.06.2011 11:39:42
Jude
Чувак, не в обиду сказано, но я смотрю на вопросы, которые ты задаешь, и нахожу что ты, как Дельфист, не стоишь 1200 долларов в Киеве. Я бы взял тебя джуниором на 500 баков с испытательным сроком.
Anatoly Podgoretsky
Дата: 07.06.2011 11:47:36
Многие хотят за 500 баков
ДжекНепотрошитель
Дата: 07.06.2011 11:58:10
Negruzzi Cristian
а если использовать reintroduce ?

...то ничего не будет. Если автор темы читал бы предупреждения компилятора, то он бы в первом случае увидел что-то типа "destructor Destroy hides inherited method", т.е. компилятор видит, что в классе есть два одноименных деструктора, причем не связанных между собой. И ненавязчиво попробует обратить на это внимание программиста. А reintroduce всего лишь дает понять компилятору, что вы сознательно делаете в классе несколько методов с одинаковыми именами без override (ну мало ли зачем вам это понадобилось), и с использованием этой директивы компилятор не будет выдавать вам предупреждения.
Jude
Дата: 07.06.2011 12:13:21
Джурджулешты
Jude
Чувак, не в обиду сказано, но я смотрю на вопросы, которые ты задаешь, и нахожу что ты, как Дельфист, не стоишь 1200 долларов в Киеве. Я бы взял тебя джуниором на 500 баков с испытательным сроком.

пока - да. я самоучка. и настал момент когда интересно проверить то, что знаю/понимаю с тем, что знают/понимают люди. на счет того, что ты упомянул - это планка к которой я стремлюсь. кстати если на испытательный срок (1 мес) накинешь 300 с перспективой + 100-200 то договоримся))).

На данном этапе мне интересно стало не как "сделать побыстрее чтоб как-то работало", а как сделать ПРАВИЛЬНО чтоб работало. Собственно и произвожу изыскания. В планах давненько было. но вот добрался только сейчас - пока немного тишина с заказми)))

За оценку - спасибо. именно этого очень не хватает, когда нужно определиться "куда двигаться дальше".

Спасибо.