TList<T> throws an exception

143 Views Asked by At

I have the following code:

program Tlist;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  madExcept,
  madLinkDisAsm,
  madListHardware,
  madListProcesses,
  madListModules,
  System.Generics.Collections,
  System.SysUtils;

type
  TMyItem = class(TInterfacedObject)
  private
    ItemPrice: Currency;
  public
    constructor Create(const aItemPrice: Currency); reintroduce;
  end;

  IMyList = interface
    ['{1D11BD29-5C1E-4171-B67E-FB9157DCB021}']
    function Reset(const aItems: Integer): Integer;
    function Add(const aPrice, aItemN: Integer): Integer;
  end;

  TMyList = class(TInterfacedObject, IMyList)
  private
    FList: array of TList<TMyItem>;
  public
    function Reset(const aItems: Integer): Integer;
    function Add(const aPrice, aItemN: Integer): Integer;
  end;

{ TMyList }

function TMyList.Add(const aPrice, aItemN: Integer): Integer;
begin
  FList[aItemN].Add(TMyItem.Create(aPrice));
  Result := FList[aItemN].Count;
end;

function TMyList.Reset(const aItems: Integer): Integer;
var
  I: Integer;
begin
  SetLength(FList, 0);
  SetLength(FList, aItems);
  for I := 0 to Length(FList) - 1 do
  begin
    FList[I] := TList<TMyItem>.Create;
  end;
  Result := Length(FList);
end;

{ TMyItem }

constructor TMyItem.Create(const aItemPrice: Currency);
begin
  ItemPrice := aItemPrice;
end;

var
  MyList: IMyList;

begin
  MyList := TMyList.Create;
  MyList.Reset(3);
  mylist.Add(1000, 1);
end.

When executed, it throws an exception linked to TList<T>.

As TList implements the reference counting and all the classes are derived from TInterfacedObject I was under the impression that the memory would be released automatically but obviously, I am wrong.

What am I doing wrong?

EDIT

Following the suggestions I came up with the following code:

program Tlist2;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  madExcept,
  madLinkDisAsm,
  madListHardware,
  madListProcesses,
  madListModules,
  System.Generics.Collections,
  System.SysUtils;

type
  IMyItem = interface
    ['{2EB53CC3-E5FC-474B-A162-30BBD2D17C48}']
  end;

  TMyItem = class(TInterfacedObject, IMyItem )
  private
    ItemPrice: Currency;
  public
    constructor Create(const aItemPrice: Currency); reintroduce;
  end;

  IMyList = interface
    ['{1D11BD29-5C1E-4171-B67E-FB9157DCB021}']
    function Reset(const aItems: Integer): Integer;
    function Add(const aPrice, aItemN: Integer): Integer;
  end;

  TMyList = class(TInterfacedObject, IMyList)
  private
    FList: array of TList<TMyItem>;
  public
    function Reset(const aItems: Integer): Integer;
    function Add(const aPrice, aItemN: Integer): Integer;
    destructor Destroy; override;
  end;

{ TMyList }

function TMyList.Add(const aPrice, aItemN: Integer): Integer;
begin
  FList[aItemN].Add(TMyItem.Create(aPrice));
  Result := FList[aItemN].Count;
end;

destructor TMyList.Destroy;
var
  I: Integer;
  a: Integer;
begin
  for I := 0 to Length(FList) - 1 do
  begin
    if FList[I].Count > 0 then
    begin
      for a := 0 to FList[I].Count - 1 do
      begin
        FList[I][a].Destroy
      end;
    end;
    FList[I].Destroy;
  end;
  inherited;
end;

function TMyList.Reset(const aItems: Integer): Integer;
var
  I: Integer;
begin
  SetLength(FList, 0);
  SetLength(FList, aItems);
  for I := 0 to Length(FList) - 1 do
  begin
    FList[I] := TList<TMyItem>.Create;
  end;
  Result := Length(FList);
end;

{ TMyItem }

constructor TMyItem.Create(const aItemPrice: Currency);
begin
  ItemPrice := aItemPrice;
end;

var
  MyList: IMyList;


begin
  MyList := TMyList.Create;
  MyList.Reset(3);
  mylist.Add(1000, 1);
end.

That works fine, no more memory leaks BUT I am under the impression that I am missing out on something here. I thought that by using interfaces and deriving my classes from TInterfacedObject the destroyer would not be necessary.

1

There are 1 best solutions below

2
pio pio On

Following the input from David and Remi I have amended my code:

program Tlist3;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  madExcept,
  madLinkDisAsm,
  madListHardware,
  madListProcesses,
  madListModules,
  System.Generics.Collections,
  System.SysUtils;

type
  IMyItem = interface
    ['{2EB53CC3-E5FC-474B-A162-30BBD2D17C48}']
  end;

  TMyItem = class(TInterfacedObject)
  private
    ItemPrice: Currency;
  public
    constructor Create(const aItemPrice: Currency); reintroduce;
  end;

  IMyList = interface
    ['{1D11BD29-5C1E-4171-B67E-FB9157DCB021}']
    function Reset(const aItems: Integer): Integer;
    function Add(const aPrice, aItemN: Integer): Integer;
  end;

  TMyList = class(TInterfacedObject, IMyList)
  private
    FList: array of TObjectList<TMyItem>;
  public
    function Reset(const aItems: Integer): Integer;
    function Add(const aPrice, aItemN: Integer): Integer;
    destructor Destroy; override;
  end;

{ TMyList }

function TMyList.Add(const aPrice, aItemN: Integer): Integer;
begin
  FList[aItemN].Add(TMyItem.Create(aPrice));
  Result := FList[aItemN].Count;
end;

destructor TMyList.Destroy;
var
  I: Integer;
  a: Integer;
begin
  if Length(FList) > 0 then
  begin
    for I := 0 to Length(FList) - 1 do
    begin
      FList[I].Free;
    end;
  end;
  inherited;
end;

function TMyList.Reset(const aItems: Integer): Integer;
var
  I: Integer;
begin
  if Length(FList) > 0 then
  begin
    for I := 0 to Length(FList) - 1 do
    begin
      FList[I].Free;
    end;
  end;
  SetLength(FList, 0);
  SetLength(FList, aItems);
  for I := 0 to Length(FList) - 1 do
  begin
    FList[I] := TObjectList<TMyItem>.Create;
  end;
  Result := Length(FList);
end;

{ TMyItem }

constructor TMyItem.Create(const aItemPrice: Currency);
begin
  ItemPrice := aItemPrice;
end;

var
  MyList: IMyList;


begin
  MyList := TMyList.Create;
  MyList.Reset(3);
  mylist.Add(1000, 1);
  mylist.Add(2000, 2);
  MyList.Reset(2);
  mylist.Add(1000, 0);
  mylist.Add(2000, 1);
end.

No memory leaks.