[fpc-pascal] memory management with open arrays and classes
Joost van der Sluis
joost at cnoc.nl
Mon Jun 23 11:21:46 CEST 2008
Op zaterdag 21-06-2008 om 19:23 uur [tijdzone -0700], schreef David
Emerson:
> 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:
I think you can find all this in the documentation, or elsewhere on the
web. (Maybe search for Delphi, instead fpc)
> 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?)
After calling setlength, the items in the array are not initialised.
Note that in the pascal language allocated memory is never
initialised/set to 0. The only exceptions are the variables in classes.
(There are some functions that allocate memory and initialise is
afterwards explicitely, see the documentation)
Initialising the strings you can do by setting them to an empty string.
(:= '')
> 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?
You can expect this, but I woudn't know.
> Or do I need to explicitly clear them myself?
>
> for i := 0 to high(str_arr) do str_arr[i] := '';
> setlength(str_arr, 0);
Better save then sorry? You can also try this yourself: write a small
console test program, and compile it with the -Gh compiler option and
run it. It will show you if there are any memory-leaks.
> 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);
Yup, it's pretty consistent.
> 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
Also here you have to destroy all the objects.
> 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?
Yes
> for i := 0 to high(str_arr_arr) do setlength (str_arr_arr[i], 0);
> // or will setlength silently do the job anyway?
No
> 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?
Yes they are. But you have to take care of the objects in the array,
offcourse. (as explained above)
> 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?
The setlengths to 0 aren't needed.
> 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?
Not if you do not have a create that creates embedded_cls. But normally:
yes.
Joost.
More information about the fpc-pascal
mailing list