How do I free the contents of a TList?

12.2k Views Asked by At

I have this code in Delphi 4. I created 10 objects of type T_Charge (see below), which I added to a TList, named myList. The T_Charge type has references to three objects of type T_Platte (see below).

What is the correct way of freeing all the memory of all 10 objects? So, for every object I want to free the memory of P1, P2, P3, F_Treated and so on, as well as the memory of myCharge itself. I tried about everything, but keep getting errors.

procedure Test;
var 
  myList: TList;
  myCharge: T_Charge;
begin
  myList := TList.Create;

  for i := 0 to 9 do
  begin;
    myCharge := T_Charge.Create(...);
    myList.Add(myCharge);
  end;
  ...
end;

type
  T_Platte = class(TObject)
  private
    F_Nummer : Integer;
    F_Los    : Integer;
    F_Dicke  : Real;
    F_Lange  : Real;
    F_Breite : Real;
    F_QuenchCode : Integer;
    F_Geschwindigkeit : Real;
    F_Menge  : Integer;
    F_MengeInLos : Integer;
    F_Treated : Boolean;
  public
    constructor Create(inNummer, inLos: Integer; inDicke, inLange, inBreite, inGeschwindigkeit : Real; inQuenchCode,inMenge: Integer; inTreated: Boolean); overload;
    constructor Create(inPlatte: T_Platte); overload;
    destructor Destroy; override;

    property Nummer: Integer read F_Nummer write F_Nummer;
    property Los: Integer read F_Los write F_Los;
    property Dicke: Real read F_Dicke write F_Dicke;
    property Lange: Real read F_Lange write F_Lange;
    property Breite: Real read F_Breite write F_Breite;
    property QuenchCode : Integer read F_QuenchCode write F_QuenchCode;
    property Geschwindigkeit: Real read F_Geschwindigkeit write F_Geschwindigkeit;
    property Menge: Integer read F_Menge write F_Menge;
    property MengeInLos: Integer read F_MengeInLos write F_MengeInLos;
    property Treated: Boolean read F_Treated write F_Treated;

    function getPlattenRecord: string;
  end; // class T_Platte  

  T_Charge = class(TObject)
  private
    F_P1 : T_Platte;
    F_P2 : T_Platte;
    F_P3 : T_Platte;
    F_Treated : Boolean;
    F_Vmin : Real;
    F_Qty : Integer;
    F_RelationNext : Boolean;
  public
    constructor Create(inP1, inP2, inP3 : T_Platte; inTreated: Boolean; inVmin: Real; inQty: Integer; inRelationNext: Boolean);  overload;
    constructor Create(inCharge : T_Charge); overload;
    destructor Destroy; override;

    property P1: T_Platte read F_P1 write F_P1;
    property P2: T_Platte read F_P2 write F_P2;
    property P3: T_Platte read F_P3 write F_P3;
    property Treated : Boolean read F_Treated write F_Treated;
    property Vmin : Real read F_Vmin write F_Vmin;
    property Qty : Integer read F_Qty write F_Qty;
    property RelationNext : Boolean read F_RelationNext write F_RelationNext;

    function getChargeRecord: string;
  end; // class T_Charge


constructor T_Platte.Create(inNummer, inLos: Integer; inDicke, inLange, inBreite, inGeschwindigkeit: Real; inQuenchCode,inMenge: Integer; inTreated: Boolean);
begin
  F_Nummer := inNummer;
  F_Los := inLos;
  F_Dicke := inDicke;
  F_Lange := inLange;
  F_Breite := inBreite;
  F_Geschwindigkeit := inGeschwindigkeit;
  F_QuenchCode := inQuenchCode;
  F_Menge := inMenge;
  F_MengeInLos := 0;
  F_Treated := inTreated;
end;

constructor T_Charge.Create(inP1, inP2, inP3 : T_Platte; inTreated: Boolean; inVmin:  Real; inQty: Integer; inRelatio nNext: Boolean);
begin
  F_P1 := T_Platte.Create(inP1);
  F_P2 := T_Platte.Create(inP2);
  F_P3 := T_Platte.Create(inP3);
  F_Treated := inTreated;
  F_Vmin := inVmin;
  F_Qty := inQty;
  F_RelationNext := inRelationNext;
end;
6

There are 6 best solutions below

0
On

Little knowledge of classes: TList vs TObjectList

TList holds Pointers so has no idea of how to free any TObject stored

TObjectList hols TObjects, so can free them and does it automatically

My best i can say: Use TObjectList instead of TList as your base class or type declaration.

Samples:

Wrong: TMyListOfObjects=class(TList) ...
 Good: TMyListOfObjects=class(TObjectList) ...

Wrong: MyListOfObjects:TList;
 Good: MyListOfObjects:TObjectList;

Note: TList is declared on unit classes and TObjectList is declared on Contnrs, that makes so many people use TList and do not know anything about TObjectList (also me before i read about TObjectList on Internet some time ago).

0
On

Before destroying myList you need:

for i := 0 to myList.Count - 1 do  
begin;
  TObject(myList[i]).Free
end;

In the destructor for T_Charge you need to free F_P1, F_P2 and F_P3.

0
On

Add the following to the destructor for T_Charge:

destructor T_Charge.Destroy;
begin
  F_P1.Free;
  F_P2.Free;
  F_P3.Free;
  // Other code as needed
  inherited;
end;

Before you free your list, loop through and delete each T_Charge:

for i := 0 to myList.Count - 1 do
  TObject(myList[i]).Free;         // This will free the inner `T_Platt3` 
                                   // each `T_Charge` contains.
3
On

Specifically when a list should hold class instances I think you should rather use TObjectList for that purpose (TObjectList). Call the constructor of the TObjectList with Create(True) which tells the list to free all contained class instances once the list gets destroyed or you remove an item from it. Nevertheless, as mentioned in Ken White's answer you still need to free F_P1 etc. in the T_Charge destructor.

0
On

Don't forget to type cast the items, since they holds a pointer without type !

for i := 0 to myList.Count - 1 do
begin
  T_Charge(myList[i]).Free;  // Type cast
end;
0
On

To free the list objects, create your list and override TList.Notify().

procedure THotFixList.Notify(Ptr: Pointer; Action: TListNotification);
begin
  inherited;
  if Action = lnDeleted then
    Tobject(Ptr).Free;
end;

It will ensure your objects are freed whenever you call TLIst.Delete, TList.Clear, TList.Free, without having to iterate the list yourself each time.