Home > database >  Delphi tstringlist.free erases result
Delphi tstringlist.free erases result

Time:01-02

Ok, this I don't understand.

   path:=tstringlist.create;
   //load up the path with stuff
   Result := path;
   path.free;
   exit; 

I would have thought that result would actually equal the path but it apparently doesn't. If I remove path.free from the code above, the result works as it should and it = the path tstringlist but I get a memory leak. When I put path.free in there, the result becomes empty. I understand the memory leak but how is result getting erased if I free the path AFTER I make it := ????

And yes, the above code is inside multiple for loops which is why I'm using exit. I've tried break and try finally and had no luck making those work either.

CodePudding user response:

The reason why Result becomes empty when you include path.free is because Result is just a reference to path. When you call path.free, you are freeing the memory that path occupies, which makes the reference to that memory invalid. When you try to access Result after freeing path, you are trying to access invalid memory, which can result in undefined behavior.

You need to free the returned TStringList outside of the function, you should modify the function as follows:

function getPath: TStringList;
begin
  Result := tstringlist.create;
  //load up the path with stuff
end;

// usage:
var
  path: TStringList;
begin
  path := getPath;
  try
    // use path here
  finally
    path.Free;
  end;
end;

This way, the returned TStringList is created inside the function and is passed as a reference to the caller. The caller is responsible for freeing the TStringList when it is no longer needed by calling Free on it. This is called "resource acquisition is initialization" (RAII) and is a common pattern in Delphi for managing resources such as dynamically allocated objects.

By using this pattern, you can ensure that the TStringList is always properly freed and avoid potential memory leaks.


More advanced trick (XE2 ):

type
  IScope<T: class> = interface
  private
    function GetIt: T;
  public
    property It: T read GetIt;
  end;

  TScope<T: class> = class(TInterfacedObject, IScope<T>)
  private
    FValue: T;
  public
    constructor Create(const AValue: T);
    destructor Destroy; override;
    function GetIt: T;
  end;

constructor TScope<T>.Create(const AValue: T);
begin
  inherited Create;
  FValue := AValue;
end;

destructor TScope<T>.Destroy;
begin
  FValue.Free;
  inherited;
end;

function TScope<T>.GetIt: T;
begin
  Result := FValue;
end;

function getPath: IScope<TStringList>;
var
  path: TStringList;
begin
  path := tstringlist.create;
  //load up the path with stuff
  Result := TScope<TStringList>.Create(path);
end;

// usage:
var
  path: TStringList;
begin
  path := getPath.It;
  // use path here
end; // auto-free here

CodePudding user response:

Let me rephrase your variable and class names and add a few comments:

function MyNewHouse(): TStringList;
var
  NewAddress: TStringList;
begin
  // Construct a house with walls, windows, doors and a roof. Those
  // are the properties and methods that we're able to use later.
  NewAddress := House.Create();

  // ...fill the house with content, using our walls, windows, doors...

  // Only copy the new house's address, not the house in its entirety.
  // And certainly not its content.
  Result := NewAddress;

  // Demolish/Tear down the house, which can only be made once. When
  // the house is demolished, you can neither access it, nor tear it
  // down anew. However, the address is still somewhat "valid". While
  // everything but the spot where it once existed is gone.
  NewAddress.Free();

  Exit;
end;

Whenever you assign variables of a class type (such as TObject or TStringList or TForm) you're merely copying its address, not its entire content. For copying (believe it or not) the method .Assign() exists:

  // Instead of only "Result := NewAddress;"
  Result.Assign( NewAddress );

That copies its whole content. This method exists for many classes, and for each different class "copying its content" can mean different things, just like you may want to copy a TStringList's items, but not necessarily its other settings. But if you wanted it that way you would have used Result.Items := NewAddress.Items already in your example...

  • Related