5
votes

What is the difference between System.GetMem and System.ReallocMem?

Delphi 2009 Help for ReallocMem, is exactly the same description of GetMem. How about System.FreeMem and System.Dispose

What should I use with arrays?

type
  PMemberDataList = ^TMemberDataList;
  TMemberDataList = array[0..MaxClassMembers -1] of PMemberData;

var
  FItems: PMemberDataList;

begin
  GetMem(FItems, Value * SizeOf(Pointer));
  FreeMem(FItems);
end;

or

begin
  ReallocMem(FItems, Value * SizeOf(Pointer));
  Dispose(FItems);
end;

SOLUTION

After people advices, I declared FItems as record type, not pointer to record, TMemberDataList as dynamic array, SetLength to (de)alloc array, New/Dispose to data

type
  PMemberDataList = ^TMemberDataList;
  TMemberDataList = array of PMemberData;
var
  Items: TMemberDataList;
  Item: PMemberData;

// Add
begin
  Setlength(Items, 1);
  New(Item);
  Items[0]:= Item
end;

// Remove
begin
  Dispose(Items[0]);
  Setlength(Items, 0);
end;
2
Based on your updated usage, it looks like the array "owns" the member data. Instead of pointing at member-data records, you could further simplify the code by making it an array of TMemberData instead of PMemberData.Rob Kennedy

2 Answers

20
votes

GetMem always allocates memory, FreeMem always releases/frees memory, ReallocMem may do one, the other, or both. In fact, when used properly, ReAllocMem is really the only memory management API needed. If you start with a nil pointer, and call ReAllocMem with a size > 0, then it acts like GetMem. If you call ReAllocMem with size = 0, then it acts like FreeMem. The only time it actually "re-allocates" memory is if the pointer is non-nil and the size > 0.

New and Dispose are designed to work with typed pointers or for you "old-skool" folks, the older Turbo Pascal object model (the old "object) syntax. New and Dispose will also ensure that any typed pointer that is a reference to a managed type will properly initialize that type. For instance given the following:

type
  PMyRec = ^TMyRec;
  TMyRec = record
    Name: string;
    Value: Variant;
  end;

var
  Rec: PMyRec;
begin
  New(Rec);
  try
    Rec.Name := 'TestValue';
    Rec.Value := 100;
    ...
  finally
    Dispose(Rec);
  end;
end;

New and Dispose will ensure that the Name and Value fields of the record are properly initialized and finalized or cleaned-up. New and Dispose, in the above case is equivalent to:

GetMem(Rec, SizeOf(Rec^));
Initialize(Rec);
...
Finalize(Rec);
FreeMem(Rec);

For the example you gave, Gamecat is right, you would probably be better off using a dynamic array since they are better managed by the compiler and they also carry their own intrinsic length. With your example, you would have to separately keep track of the number of items in the array, such that wherever you passed around the array, you'd also have to pass around the currently allocate length. By using a dynamic array, all the information is kept neatly packaged together. This would allow you to iterate over the array regardless of the current length by simply doing one of the following:

var
  Member: TMemberData;
  Items: array of TMemberData;
  ...
begin
  SetLength(Items, Value);
  for Member in Items do  // iterate over each element in the array
  ...
  for Low(Items) to High(Items) do // same as above only using std functions
  ...
end;

Finally, another reason you would probably want to use a dynamic array is that if TMemberData contained strings, variants, interfaces or other "managed" types, they will be properly initialized and finalized without the need to do that manually.

3
votes

GetMem allocates a block of memory. ReallocMem re-allocates a block of memory.

But you better use dynamic arrays:

var
  FItems : array of TMemberDataList;

begin
  SetLength(FItems, Value);
end;

Its more the Delphi way.

Example

You could either do:

type
  TMemberDataList = array[0..MaxClassMembers -1] of TMemberData;

var
  FItems: TMemberDataList;
begin
  // Don't need to allocate FItems
end;

Or:

type
  TMemberDataList = array of TMemberData;

var
  FItems: TMemberDataList;
begin
  SetLength(FItems, MaxClassMembers);
end;

Class variables are pointers. So you don't have to use explicit pointers like we had to do with TP. You still can use pointers to records or objects, but there is no reason to do that.