Recent

Author Topic: freeing objects  (Read 1776 times)

Bogen85

  • Hero Member
  • *****
  • Posts: 595
Re: freeing objects
« Reply #15 on: February 15, 2023, 07:51:35 pm »
The problem I see is, what if you don't use the constructor directly, or as an iterator, but you have an iterator that includes a managed type. This might also trigger the bug, as the management handling routines are recursive to all the subfields.

Yeah, I'm looking that enumeration double finalize now.
The last finalize is the valid one. The first finalize is bogus and should not have occurred. Which to me still points to a constructor issue (where an unused construction is being superseded before use).

I agree is it is serious bug and is not always going to be harmless.

Not sure how I'm going to be able to work around it cleanly if I run into it, which has not happened yet.


Bogen85

  • Hero Member
  • *****
  • Posts: 595
Re: freeing objects
« Reply #16 on: February 15, 2023, 07:57:20 pm »
In the original example inherited Create should be done before other operations  in class A and B’s create method.

You mean as follows?

Code: Pascal  [Select][+][-]
  1. program MyLazTest;
  2.  
  3. {$mode objfpc}{$M+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  7.   cthreads,
  8.   {$ENDIF}{$ENDIF}
  9.   Classes, SysUtils, CustApp
  10.   { you can add units after this };
  11.  
  12. type
  13.   { My B }
  14.   B = class( TObject)
  15.   public
  16.     constructor Create(Sender : TObject);
  17.     destructor Destroy; override;
  18.   end;
  19.  
  20.   { My A }
  21.  
  22.   A = class( TObject)
  23.     myB_in_A : B;
  24.   public
  25.     constructor Create;
  26.     destructor Destroy; override;
  27.   end;
  28.  
  29.   { MyApplication_1 }
  30.  
  31.   MyApplication_1 = class(TCustomApplication)
  32.     myA : A;
  33.   protected
  34.     procedure DoRun; override;
  35.   public
  36.     constructor Create(TheOwner: TComponent); override;
  37.     destructor Destroy; override;
  38.     procedure WriteHelp; virtual;
  39.   end;
  40.  
  41.   constructor A.Create;
  42.   begin
  43.     inherited Create(); // <-- here
  44.     WriteLn('Object of Class A generated.');
  45.     myB_in_A := B.Create(Self);
  46.   end;
  47.  
  48. destructor A.Destroy;
  49. begin
  50.  
  51.   //myB_in_A.Destroy;
  52.   WriteLn('Object of Class A released.');
  53.   inherited Destroy;
  54. end;
  55.  
  56. constructor B.Create(Sender : Tobject);
  57. begin
  58.   inherited Create(); // <-- here
  59.   WriteLn('Object of Class B generated.');
  60. end;
  61.  
  62. destructor B.Destroy;
  63. begin
  64.   WriteLn('Object of Class B released.');
  65.   inherited Destroy;
  66.  
  67. end;
  68.  
  69. { MyApplication_1 }
  70.  
  71. procedure MyApplication_1.DoRun;
  72. var
  73.   ErrorMsg: String;
  74. begin
  75.   // quick check parameters
  76.   ErrorMsg:=CheckOptions('h', 'help');
  77.   if ErrorMsg<>'' then begin
  78.     ShowException(Exception.Create(ErrorMsg));
  79.     Terminate;
  80.     Exit;
  81.   end;
  82.  
  83.   // parse parameters
  84.   if HasOption('h', 'help') then begin
  85.     WriteHelp;
  86.     Terminate;
  87.     Exit;
  88.   end;
  89.   myA := A.create;
  90.   Sleep(1000);
  91.   WriteLn('Hallo...');
  92.   myA.destroy; // here I expect both destructors called
  93.   { add your program here }
  94.   Sleep(2000);
  95.   // stop program loop
  96.   Terminate;
  97. end;
  98.  
  99. constructor MyApplication_1.Create(TheOwner: TComponent);
  100. begin
  101.   inherited Create(TheOwner);
  102.   StopOnException:=True;
  103. end;
  104.  
  105. destructor MyApplication_1.Destroy;
  106. begin
  107.   inherited Destroy;
  108. end;
  109.  
  110. procedure MyApplication_1.WriteHelp;
  111. begin
  112.   { add your help code here }
  113.   writeln('Usage: ', ExeName, ' -h');
  114. end;
  115.  
  116. var
  117.   Application: MyApplication_1;
  118. begin
  119.   Application:=MyApplication_1.Create(nil);
  120.   Application.Title:='My Application';
  121.   Application.Run;
  122.   Application.Free;
  123. end.

ppan

  • New Member
  • *
  • Posts: 13
Re: freeing objects
« Reply #17 on: February 16, 2023, 08:22:44 am »
Hi, I thank you all for your Input!

In the original example inherited Create should be done before other operations  in class A and B’s create method.

In my Programm the destructor is not called anyway. Is there something wrong in my code?

Code: Pascal  [Select][+][-]
  1. program MyLazTest;
  2.  
  3. {$mode Delphi}{$M+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  7.   cthreads,
  8.   {$ENDIF}{$ENDIF}
  9.   Classes, SysUtils, CustApp
  10.   { you can add units after this };
  11.  
  12. type
  13.   { My B }
  14.   B = class( TObject)
  15.   public
  16.     constructor Create(Sender : TObject);
  17.     destructor Destroy; override;
  18.   end;
  19.  
  20.   { My A }
  21.  
  22.   A = class( TObject)
  23.     myB_in_A : B;
  24.   public
  25.     constructor Create(Sender : TObject);
  26.     destructor Destroy; override;
  27.   end;
  28.  
  29.   { MyApplication_1 }
  30.  
  31.   MyApplication_1 = class(TCustomApplication)
  32.     myA : A;
  33.   protected
  34.     procedure DoRun; override;
  35.   public
  36.     constructor Create(TheOwner: TComponent); override;
  37.     destructor Destroy; override;
  38.     procedure WriteHelp; virtual;
  39.   end;
  40.  
  41.   constructor A.Create(Sender : TObject);
  42.   begin
  43.     inherited Create();
  44.     WriteLn('Object of Class A generated.');
  45.     myB_in_A := B.Create(Self);
  46.   end;
  47.  
  48. destructor A.Destroy;
  49. begin
  50.  
  51.   //myB_in_A.Destroy;
  52.   WriteLn('Object of Class A released.');
  53.   inherited Destroy;
  54. end;
  55.  
  56. constructor B.Create(Sender : TObject);
  57. begin
  58.   inherited Create();
  59.   WriteLn('Object of Class B generated.');
  60.  
  61. end;
  62.  
  63. destructor B.Destroy;
  64. begin
  65.   WriteLn('Object of Class B released.');
  66.   inherited Destroy;
  67.  
  68. end;
  69.  
  70. { MyApplication_1 }
  71.  
  72. procedure MyApplication_1.DoRun;
  73. var
  74.   ErrorMsg: String;
  75. begin
  76.   // quick check parameters
  77.   ErrorMsg:=CheckOptions('h', 'help');
  78.   if ErrorMsg<>'' then begin
  79.     ShowException(Exception.Create(ErrorMsg));
  80.     Terminate;
  81.     Exit;
  82.   end;
  83.  
  84.   // parse parameters
  85.   if HasOption('h', 'help') then begin
  86.     WriteHelp;
  87.     Terminate;
  88.     Exit;
  89.   end;
  90.   myA := A.create(Self);
  91.   Sleep(1000);
  92.   WriteLn('Hallo...');
  93.   myA.free; // here I expect both destructors called
  94.   { add your program here }
  95.   Sleep(2000);
  96.   // stop program loop
  97.   Terminate;
  98. end;
  99.  
  100. constructor MyApplication_1.Create(TheOwner: TComponent);
  101. begin
  102.   inherited Create(TheOwner);
  103.   StopOnException:=True;
  104. end;
  105.  
  106. destructor MyApplication_1.Destroy;
  107. begin
  108.   inherited Destroy;
  109. end;
  110.  
  111. procedure MyApplication_1.WriteHelp;
  112. begin
  113.   { add your help code here }
  114.   writeln('Usage: ', ExeName, ' -h');
  115. end;
  116.  
  117. var
  118.   Application: MyApplication_1;
  119. begin
  120.   Application:=MyApplication_1.Create(nil);
  121.   Application.Title:='My Application';
  122.   Application.Run;
  123.   Application.Free;
  124. end.
  125.  
  126.  

This is very interesting! Im not in in interfaces, I have to read about it. It is clear for me that the management could be done by myself (though I dont have any clue how to do it) but I thought it should be done by the language. Thank you for this informations, but I'm not sure if managing memory is THAT fun  ;).

Freeing is automated for some instances, COM interfaces, dynamic arrays and strings are automatically managed through reference counting, which is how it is also often done in other languages such as swift. But because when developing Delphi with it's new class based OOP system, Embarcadero decided that managing memory manually is so fun that they don't want to include it for classes (even though as already mentioned it is already widely used for other types), we are now stuck with it for all class instances.

Automatic memory management can be emulated, by using Interfaces (as with in the Link that Bogen85 posted), or the new managed records (even though they still have the double finalize bug), but it always requires encapsulation into managed types.

When you write your own class, you can write an interface for it and only use it through that COM interface. This is what I did for my Iterator library because it allows to use temporary objects without the need of manual freeing. Which allows doing things like:
Code: Pascal  [Select][+][-]
  1.   for i in Filter<Integer>(Iterate<Integer>(Data), isEven) do
  2.     Write(' ', i);
instead of:
Code: Pascal  [Select][+][-]
  1. DataIterator := Iterate<Integer>(Data);
  2. FilterIterator := Filter<Integer>(DataIterator, isEven);
  3. try
  4.   for i in FilterIterator do
  5.     Write(' ', i);
  6. finally
  7.   FilterIterator.Free;
  8.   DataIterator.Free;
  9. end;
It's just much cleaner.


Thaddy

  • Hero Member
  • *****
  • Posts: 14382
  • Sensorship about opinions does not belong here.
Re: freeing objects
« Reply #18 on: February 16, 2023, 09:18:32 am »
At least this is wrong:
Code: Pascal  [Select][+][-]
  1. destructor A.Destroy;
  2. begin
  3.  
  4.   //myB_in_A.Destroy;
  5.   WriteLn('Object of Class A released.');
  6.   inherited Destroy;
  7. end;
Should be:
Code: Pascal  [Select][+][-]
  1. destructor A.Destroy;
  2. begin
  3.   myB_in_A.Free;
  4.   WriteLn('Object of Class A released.');
  5.   inherited Destroy;
  6. end;
As pointed out earlier, call free, not destroy, on classes (but do so on old school objects). Free will implicitly call destroy and cleans up.
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

 

TinyPortal © 2005-2018