如何获得ByVal函数结果而不是ByRef?

时间:2015-07-10 12:09:53

标签: delphi oop garbage-collection function-calls

受到以下帖子的启发:

我想知道,如果有任何选项明确告诉函数我想要ByVal结果而不是ByRef?有没有更简单的方法来将对象的所有权从初始调用者转移到函数然后再返回。

考虑一个在运行时创建对象的函数,然后在Result中将引用传递给该对象。调用这样的函数后,主代码继续运行并“忘记”正确处理对象......Voilà,内存泄漏。如果主代码释放由另一个函数创建的对象,这看起来也很奇怪。

另一方面,如果对象在函数内被释放,那么引用也会被破坏,这会导致AV。

为什么不在主代码中声明一个'虚拟'(未初始化)对象并使用函数结果对虚拟对象进行初始化?那么我唯一需要注意的事情就是在我的表格OnDestroy中释放一个虚拟对象。该函数的代码可以移动到一个单独的单元,其中所有创建/销毁内容将由initialization / finalization块或AfterCreation / BeforeDestruction事件处理。

我唯一需要的是确保我的函数返回ByVal结果而不是ByRef。因此,结果(对象)只是复制到一个虚拟对象,然后在一个函数内被释放。

IMO这可以比将虚拟对象或TComponent所有者作为参数传递更容易管理和阅读。

2 个答案:

答案 0 :(得分:1)

据我所知,从问题和评论来看,你不想在函数内部创建一个新对象并返回它。您希望调用函数的最终结果是由调用者控制的对象已被函数修改,即被调用者。

鉴于您要修改现有对象,我看到您已经识别的选项的替代方案。即,将对象传递给函数,然后让函数修改该对象。函数返回时,对象已更新。

这是唯一可行的选择源于类是引用类型。所以当你写:

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

然后你只复制一个引用。参考类型无法与之相提并论。

如果要分配导致值赋值,则需要使用值类型。也就是说,记录。

答案 1 :(得分:0)

对象总是通过引用。你持有一个指向对象的指针。价值没有。如果您担心清理,请查看TInterfacedObjects。当它们设置为零或超出范围时处理清理。

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.