如何在函数之间传递对象?

How do you pass objects between functions?

假设我有一个函数:

function someFunction: TStringList;  
begin  
  result:=TStringList.Create;  
  if someConditionIsTrue then  
    result:=doSomething;  
  //other code  
end; 

函数doSomething:

function doSomething: TStringList;
begin
  result:=TStringList.Create;
  result.Add(something);
end;

如果我 运行 此代码一切正常,但我仍然想知道这是否是 "proper" 传递对象(如字符串列表)的方法?

字符串列表永远不会被释放,我想知道在调试或其他试图理解代码的人时,以这种方式传递对象是否会变得复杂或混乱。

"proper" 方法是让您建立自己的销毁规则。在函数结果中创建对象很好,但前提是您要遵循自己的严格规则。

在您的情况下,SomeFunction 存在内存泄漏。首先,您创建一个 TStringList,然后如果满足某些条件,您将在其位置创建另一个 TStringList,完全忽略第一个。因此,内存泄漏。

DoSomething 不应该是一个返回字符串列表的函数,如果您可能已经创建了一个字符串列表的话。相反,只需将其作为一个程序...

procedure DoSomething(AList: TStringList);
begin
  AList.Add(Something);
end;

完成后,SomeFunction 应该如下所示:

function someFunction: TStringList;  
begin  
  Result:= TStringList.Create;  
  if someConditionIsTrue then  
    DoSomething(Result);  
  //other code  
end; 

"The stringlists are never freed"

我希望这不是设计使然。您创建的所有内容都必须在某个时候被释放,尤其是当您拥有创建其结果的函数时。唯一的例外是,如果您创建的东西在应用程序的整个持续时间内都存在,即使如此,无论如何释放它们也是极端​​的共同点。


关于这一点,我唯一一次在函数结果中创建对象是在封装多行代码时,否则这些代码会被重复多次。例如,创建查询。

与其重复此代码...

Q:= TADOQuery.Create(nil);
Q.Connection:= MyDatabaseConnection;
Q.SetSomeOtherProperties;

...我把它放在函数中...

function CreateQuery: TADOQuery;
begin
  Result:= TADOQuery.Create(nil);
  Result.Connection:= MyDatabaseConnection;
  Result.SetSomeOtherProperties;
end;

然后,只要我需要重复该代码,我就可以简单地调用这个函数...

Q:= CreateQuery;

The stringlists are never freed

这本身就是一个问题。就像评论中提到的那样,这会造成内存泄漏。总的来说,我不赞成创建对象并通过其结果赋予所有权的函数。当我需要这样做时,我通常将我的函数命名为 "Create*" 以尽可能明确地表明调用者负责释放内存。

话虽如此,一个更优雅的模式可以满足您的需求:

procedure someFunction;  
var vStrings : TStringList;
begin  
  vStrings := TStringList.Create;  
  try
    if someConditionIsTrue then  
      doSomething(vStrings);  
    //other code  
  finally
    vStrings.Free;
  end;
end;

procedure doSomething(AStrings : TStringList);
begin
  AStrings.Add(something);
end;

如果您确实需要 "someFunction" 到 return 一个 TStringList 并且不想通过参数接收一个,请参阅此处如何正确管理它以避免内存泄漏。

function CreateAndInitStrings : TStringList;  
begin  
  Result := TStringList.Create;  
  try
    if someConditionIsTrue then  
      doSomething(Result);  
    //other code  
  except
    Result.Free;
    raise;
  end;
end;