0

Inspired by the following posts:

I wonder, if there is any option to explicitly tell a function that I want a ByVal result instead of ByRef? Is there any simpler approach to transfer an object's ownership from initial caller to a function and then back.

Consider a function which creates an object at runtime and then passes the reference to this object in a Result. After calling such a function, the main code keeps on running and 'forgets' to correctly dispose the object... Voilà, a memory leak. This would also look weird if main code frees an object created by another function.

On the other hand, if the object is freed inside the function, then the reference also gets destroyed and this leads to AV.

Why not just declare a 'dummy' (uninitialized) object in the main code and inizialize a dummy object with a function result? Then the only thing I must take care of -- is to free a dummy object in my Form's OnDestroy. The function's code can be moved to a separate unit where all creation/destruction stuff will be taken care of by initialization/finalization blocks or AfterCreation/BeforeDestruction events.

The only thing I need -- is to make sure my function returns a ByVal result instead of ByRef. So the result (object) is merely copied to a dummy object and then gets freed inside a function.

IMO this could be much easier to manage and to read than passing a dummy object or TComponent owner to function as a parameter.

Community
  • 1
  • 1
Interface Unknown
  • 713
  • 10
  • 26
  • I'm not quite sure what you are looking for here. If the object is destroyed in the function, then you cannot return it. So let's rule out that option. Which leaves two options: 1. Caller supplies an object to be populated by callee. 2. Callee creates new object and returns it to caller. In both cases the caller is responsible for lifetime. What exactly is the problem? – David Heffernan Jul 10 '15 at 12:12
  • #2. "Callee creates new object and returns it to caller..." It returns a _refernce_ to an object. I mean, the object is not assigned/copied to caller's object. – Interface Unknown Jul 10 '15 at 12:22
  • 3
    Classes are references types. You can't change that. If you want to take a copy, take a copy. You'll need to use a mechanism like `Assign` to make that happen. If you want the callee to populate the caller's object, pass that in as a parameter. – David Heffernan Jul 10 '15 at 12:25
  • Your question makes no sense. Your *dummy object* can be the actual object returned by the function *Result*, and you can free that object in your Form's `OnDestroy`. Your suggested *improvement* is actually worse, in that it complicates the code unnecessarily and accomplishes zero. – Ken White Jul 10 '15 at 12:49

2 Answers2

1

As far as I can tell, judging from the question and comments, you don't want to create a new object inside the function and return that. You want the net result of calling the function is for an object controlled by the caller to have been modified by the function, that is the callee.

Given that you want to modify an existing object, I see little alternative to the option that you have already identified. Namely, pass the object to the function, and let the function modify that object. When the function returns, the object has been updated.

That this is the only viable option stems from classes being reference types. So when you write:

var
  obj: TMyObject; // where TMyObject is a class
....
obj := foo(...);

then you are copying a reference only. There's no getting away from that with reference types.

If you want assignment to result in a value assignment you need to use a value type. That is, a record.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
0

Objects are always by reference. You hold a pointer to an Object. There is no by Value. If you are worried about cleanup look at TInterfacedObjects. Handle clean up when they are set to nil or fall out of scope.

unit Unit12;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm12 = class(TForm)
    btnObject: TButton;
    btnInterface: TButton;
    procedure btnObjectClick(Sender: TObject);
    procedure btnInterfaceClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  IMyInterface = interface
    function GetName: string;
    procedure SetName(const Value: string);
    property name: string read GetName write SetName;
  end;

  TMyObject = class(TInterfacedObject, IMyInterface)
  private
    FName: string;
    function GetName: string;
    procedure SetName(const Value: string);
  published
    property name: string read GetName write SetName;
  end;

var
  Form12: TForm12;

implementation

{$R *.dfm}

function MyObject(aMyObject: TMyObject): TMyObject;
begin
  if not Assigned(aMyObject) then
    Result := TMyObject.Create
  else
    Result := aMyObject;
//Code to work with Result below here...
end;

function MyInterface(aMyInterface: IMyInterface): IMyInterface;
begin
  if not Assigned(aMyInterface) then
    Result := TMyObject.Create
  else
    Result := aMyInterface;
//Code to work with Result below here...
end;

{ TMyObject }

function TMyObject.GetName: string;
begin
  Result := FName;
end;

procedure TMyObject.SetName(const Value: string);
begin
  FName := Trim(Value);
end;

procedure TForm12.btnInterfaceClick(Sender: TObject);
{InterfacedObjects handle clean up when they are set to nil or go out of scope}
var
  a_Interface: IMyInterface;
begin
  a_Interface := MyInterface(nil);
  a_Interface.Name := 'MyInterface1   ';
  ShowMessage(a_Interface.Name);
end;

procedure TForm12.btnObjectClick(Sender: TObject);
var
  a_Object: TMyObject;
begin
{There is no by Value for objects...because Objects are pointers and always
 by Reference.  Someone is responsible for cleaning up}
  a_Object := MyObject(nil);
  try
    a_Object.Name := 'MyObject1       ';
    ShowMessage(a_Object.Name);
  finally
    a_Object.Free;
  end;
end;

end.
House of Dexter
  • 386
  • 1
  • 7