tags:

views:

275

answers:

4

Below, I inserted a code written by Ray Konopka (part of the Coderage presentation). I am planning to use it, however, I am not sure how to clean (on the fly) multiple objects. All my attempts were unsucesfull and rendered memory leak. Any thoughts are appreciated. Thanks,

  program stringlistDictionary;

{$APPTYPE CONSOLE}

uses
 Classes,
 SysUtils;

 type
   TPlayer = class
  public
   Name: string;
   Position: string;
   Hits: Integer;
   AtBats: Integer;
   constructor Create( Name, Position: string );
 end;


   constructor TPlayer.Create( Name, Position: string );
    begin
      inherited Create;
      Self.Name := Name;
      Self.Position := Position;
      Hits := 0;
      AtBats := 0;
    end;


    var
      Team: TStringList;
      Player, NewPlayer: TPlayer;
      I: Integer;


    function FindPlayer( const Name: string ): TPlayer;
    var
      Idx: Integer;
    begin
      Result := nil;
      if Team.Find( Name, Idx ) then
        Result := TPlayer( Team.Objects[ Idx ] );
    end;


    begin {== Main ==}

      Writeln( 'StringList Dictionary' );
      Writeln( '---------------------' );
      Writeln;

      Team := TStringList.Create;
      try
        NewPlayer := TPlayer.Create( 'Aramis Ramerez', 'Third Base' );
        NewPlayer.Hits := 120;
        NewPlayer.AtBats := 350;

        Team.AddObject( NewPlayer.Name, NewPlayer );

        NewPlayer := TPlayer.Create( 'Derrick Lee', 'First Base' );
        NewPlayer.Hits := 143;
        NewPlayer.AtBats := 329;

        Team.AddObject( NewPlayer.Name, NewPlayer );

        NewPlayer := TPlayer.Create( 'Ryan Theriot', 'Short Stop' );
        NewPlayer.Hits := 87;
        NewPlayer.AtBats := 203;

        Team.AddObject( NewPlayer.Name, NewPlayer );

        Player := FindPlayer( 'Derrick Lee' );
        if Player <> nil then
          Writeln( 'Player Found: ', Player.Name, ', ', Player.Position )
        else
          Writeln( 'Player not found.' );
        Writeln;

        Writeln( 'Active Roster' );
        Writeln( '-------------' );

        for I := 0 to Team.Count - 1 do
          Writeln( TPlayer( Team.Objects[ I ] ).Name, #9,
                   TPlayer( Team.Objects[ I ] ).Position );

        Readln;

      finally
        //!! Need to free the players.
        Team.Free;
      end;

    end.
+12  A: 

With Delphi 2009, the TStringList constructor has an optional boolean parameter "OwnsObjects". If you set that to true, the objects are freed automatically.

Else you can do the following:

for i := Team.Count-1 downto 0 do begin
  Team.Objects.Free;
end;
Team.Free;

And by the way, public fields are discouraged. You beter use properties so you can control what access is possible to the fields. And you can add setter functions to validate the input.

type
  TPlayer = class
  private
    FName     : string;
    FPosition : string;
    FHits     : Integer;
    FAtBats   : Integer;
  public
    constructor Create(const AName, APosition: string );

    property Name: string read FName;
    property Position: string read FPosition;
    property Hits: Integer read FHits write FHits;
    property AtBats: Integer read FAtBats write FAtBats;
 end;
Gamecat
THANK YOU. I have tested your solution in my applications and all the objects were destroyed. I really appreciated.
Greener
+3  A: 

just a clarification about gamecat answer: I don't know about delphi 2009 but usually the Objects property need an index, and you don't really need a reverse cycle, so:

for i := 0 to Team.Count-1 do
  Team.Objects[i].Free;
Team.Free;

or:

while Team.Count > 0 do
begin
  Team.Objects[0].Free;
  Team.Delete(0);
end;
Team.Free;
Giorgio Gelardi
Is the "Team.Delete(0);" statement necessary?
Argalatyr
The way he's got it written, yes.
Mason Wheeler
@Mason: you're quite right - I don't usually do it that way.
Argalatyr
It's not optimal, specially for large collections. The first code snop is better IMHO.
Marco van de Voort
+3  A: 

Kinda obvious, but still - you don't have to write 'for ... Free' code every time you want to clear TStringList objects. You can put it into a global function.

procedure FreeObjects(sl: TStringList);
var
  i: integer;
begin
  for i := 0 to sl.Count - 1 do 
    sl.Objects[i].Free;
end;

FreeObjects(Team);

Or you can put it into a TStringList helper.

TStringListHelper = class helper for TStringList
public
  procedure FreeObjects;
end;

procedure TStringListHelper.FreeObjects;
var
  i: integer;
begin
  for i := 0 to Count - 1 do 
    Objects[i].Free;
end;

Team.FreeObjects;
gabr
IIRC, class helpers can be broken easily without even a compiler warning.
mjustin
Yes, class helpers are problematic - if you have multiple helpers for one class, the last one will always override all others. Still, they are useful.
gabr
@Gabr: Thanks for the useful code example. I did not know that a class helper can used this way.
Greener
I wouldn't use classhelpers if I had any other choice. In own code (that doesn't interact heavily with the VCL), it is easier to simply create a derivate and override destroy, like Gedean says below.
Marco van de Voort
+1  A: 

Using D7, I can just subclass TStingList

Gedean Dias
Hopefully you can do that in almost any object-oriented language...but thinking about the STL...maybe not
Smasher