[fpc-pascal] memory management with open arrays and classes

David Emerson dle3ab at angelbase.com
Sun Jun 22 04:23:04 CEST 2008


Hi all,

I'm pretty new to both "open arrays" and OOP, and am feeling a bit 
concerned about memory management and leaks in my code. I will jump 
straight into examples and questions:


type
   T_my_object = object (TObject)
      {...}
   end;

   T_my_class = class (TComponent)
      {...}
   end;

type
   str_array_type = array of ansistring;
   int_array_type = array of integer;
   obj_array_type = array of T_my_object;
   cls_array_type = array of T_my_class;

var
   str_arr : str_array_type;
   int_arr : int_array_type;
   obj_arr : obj_array_type;
   cls_arr : cls_array_type;

begin
   {...}
   setlength(str_arr, some_maximum);
   setlength(int_arr, some_maximum);
   setlength(obj_arr, some_maximum);
   setlength(cls_arr, some_maximum);
   {...}


First: what does setlength do about initialization? There are a couple 
different cases here...


1. If the setlength function is creating an array of things which happen 
to be stored as pointers (ansistrings in str_arr or class instances in 
cls_arr) then will the pointers be initialized to nil by setlength? Or 
do I need to manually initialize all pointers to nil, after calling 
setlength? (I guess for ansistrings that would require using 
fillchar ... but maybe setlength is already doing this?)


2. Of course the primary concern is cleaning up. For the ansistring 
case, can I simply call:

   setlength (str_arr, 0);

...and expect that the ansistring references will be properly 
decremented and cleaned up?
Or do I need to explicitly clear them myself?

   for i := 0 to high(str_arr) do str_arr[i] := '';
   setlength(str_arr, 0);


3. Similarly for the cls_arr, do I need to explicitly call the 
destructor?

   for i := 0 to high(cls_arr) do
      if cls_arr[i] <> nil then cls_arr[i].Destroy;
   setlength(cls_arr, 0);

...or will setlength call the destructor when the array is shrunk? 
(Assumably only for non-nil pointers :)


As for the other types... for int_arr it must be a non-issue, and for 
obj_arr ... objects DO need to be destroyed! Although this is only a 
matter of curiousity, since I'm not using any objects that aren't 
classes


And of course the "array in array" example comes to mind:

var
   str_arr_arr : array of str_arr;
begin
   setlength (str_arr_arr, 5);
   setlength (str_arr_arr[3], 17);
   {...}
   // is it necessary to free all of the members as seen below?
   for i := 0 to high(str_arr_arr) do setlength (str_arr_arr[i], 0);   
   // or will setlength silently do the job anyway?
   setlength (str_arr_arr, 0);



Conversely, do open arrays get automatically cleaned up as part of an 
object destruction, or do I need to write that into the destructor?

type
   T_my_other_class = class (TComponent)
      private
         str_arr : str_array_type;
         int_arr : int_array_type;
         cls_arr : cls_array_type;
      public
         constructor Create (my_owner : TComponent; pass_size : 
integer); override;
         destructor Destroy;
      end;

constructor T_my_other_class.Create (my_owner : TComponent; pass_size : 
integer);
   begin
      inherited Create (my_owner);
      setlength (str_arr, pass_size);
      setlength (int_arr, pass_size);
   end;

destructor T_my_other_class.Destroy;
   begin
      for i := 0 to high(str_arr) do str_arr[i] := '';   // again :)
      setlength (str_arr, 0);
      setlength (int_arr, 0);
      // and what about cls_arr, even more messy of course :)
      inherited Destroy;
   end;


4. Is the above destructor actually doing anything that doesn't happen 
automatically?


And the last case, objects embedded in objects:

type
   T_my_third_class = class (TComponent)
      embedded_cls : T_my_other_class;
      bool_arr : bool_array_type;
      {...}
   end;


5. Do I need to override the destructor with one that explicitly calls 
embedded_cls.Destroy?


Of course, I didn't ask until I was already knee-deep in several layers 
of this stuff...


type

   third_class_array_type = array of T_my_third_class;

   T_group_array = class(TComponent)
      third_arr : third_class_array_type
      {...}
   end;

var
   g_arr : array of T_group_array;


... and so on. Actually, I think that's as many layers as I've got! 
whew.

Happily the program runs! I am doing a lot of this cleanup manually, 
though, and am now wondering if I should cut some of my cleanup code 
out as extraneous, or if I rather need to triple-check it and perhaps 
reinforce it with yet more cleanup code.


Thanks!

~David.





More information about the fpc-pascal mailing list