这与此问题类似.我问"为什么?" 以最流行的反应,但我不知道任何人会永远看着它一次.至少不是及时的.
无论如何,我的问题是关于将对象创建的责任委托给函数或过程的最佳实践,而不会导致内存泄漏.看来这个:
procedure FillObject(MyObject: TMyObject; SomeParam: Integer);
begin
//Database operations to fill object
end;
procedure CallUsingProcedure();
var
MyObject: TMyObject;
begin
MyObject = TMyObject.Create();
try
FillObject(MyObject, 1);
//use object
finally
MyObject.Free();
end;
end;
Run Code Online (Sandbox Code Playgroud)
比这更受欢迎:
function CreateMyObject(DBID: Integer): TMyObject;
begin
Result := TMyObject.Create();
try
//Database operations to fill object
except on E: Exception do
begin
Result.Free();
raise;
end;
end;
end;
procedure CallUsingFunction();
var
MyObject: TMyObject;
begin
MyObject = CreateMyObject(1);
try
//use object
finally
MyObject.Free();
end;
end;
Run Code Online (Sandbox Code Playgroud)
为什么?
我对Delphi比较陌生,以前在Java和PHP以及C++方面工作最多,但程度较小.直觉上,我倾向于函数方法,因为:
我不是说我是对的.我只是想了解为什么社区选择这种方法,以及是否有充分的理由让我改变.
编辑: 评论中对@ E-Rock的引用是给我的(Eric G).我改变了显示名称.
Rud*_*uis 15
Ken White写的一个问题是:你将函数的用户交给他或她必须释放的对象.
过程的另一个优点是,您可以传递层次结构中的多个对象,而创建此类对象的函数始终生成相同的对象.例如
procedure PopulateStrings(Strings: TStrings);
Run Code Online (Sandbox Code Playgroud)
该程序,你可以通过任何类型的字符串列表,无论是线 A的TMemo,该项目一的一个TListBox或TComboBox或简单的独立TStringList中.如果你有一个功能:
function CreateStrings: TStrings;
Run Code Online (Sandbox Code Playgroud)
你总是得到相同类型的对象(这个对象完全不知道,因为TStrings是抽象的,所以你可能得到一个TStringList),并且必须将内容Assign()分配给你想要修改的TStrings.该程序是首选,IMO.
此外,如果您是该函数的作者,则无法控制是否释放您创建的对象,或何时释放.如果你编写了一个程序,那么这个问题就会被取消,因为用户提供了这个对象,它的生命周期并不是你所关心的.而且您不必知道对象的确切类型,它必须只是参数的类或后代.IOW,对函数的作者来说也好得多.
出于所有原因,IMO很少从一个函数返回一个对象.甲过程仅修改该对象具有在对象上没有依赖性和为用户创建没有依赖性.
FWIW,另一个问题是如果你从DLL那样做.返回的对象使用DLL的内存管理器,以及它指向的VMT在DLL中.这意味着使用as或is在用户代码中的代码无法正常工作(因为is并as使用VMT指针来检查类标识).如果用户必须将他的对象传递给程序,则不会出现该问题.
正如其他人评论的那样,将对象传递给DLL也不是一个好主意.非虚函数将调用DLL中的函数并使用其内存管理器,这也可能导致麻烦.而is和as不正确的DLL里面工作的.因此,不要将对象传入或传出DLL.这与DLLs应该只使用POD类型参数(或复合类型 - 数组,记录 - 仅包含POD类型)或COM接口的maxime相关.COM接口也应该只使用相同类型的参数.
Ken*_*ite 13
创建对象实例并将其传递到另一个过程可以清楚地说明哪些代码负责释放实例.
在第一种情况下(使用程序填充它):
MyObj := TMyObject.Create;
try
// Do whatever with MyObj
finally
MyObj.Free;
end;
Run Code Online (Sandbox Code Playgroud)
很明显,这段代码负责MyObj在完成使用时释放.
MyObj := CreateMyObject(DBID);
Run Code Online (Sandbox Code Playgroud)
什么代码应该释放它?什么时候可以安全地释放它?谁负责异常处理?你怎么知道(作为别人代码的用户)?
作为一般规则,您应该在需要的地方创建,使用和释放对象实例.这使您的代码更易于维护,并且肯定会使以后出现的人更容易并且必须尝试解决它.:)
我使用两种成语的组合.将对象作为可选参数传递,如果未传递,则创建对象.并且在任何一种情况下都将对象作为函数结果返回.
该技术具有(1)在被调用函数内部创建对象的灵活性,以及(2)将对象作为参数传递的调用者的调用者控制.控制有两个含义:控制所用对象的实际类型,并控制何时释放对象.
这段简单的代码就是这个习惯用语的例证.
function MakeList(aList:TStrings = nil):TStrings;
var s:TStrings;
begin
s:=aList;
if s=nil then
s:=TSTringList.Create;
s.Add('Adam');
s.Add('Eva');
result:=s;
end;
Run Code Online (Sandbox Code Playgroud)
以下是三种不同的使用方法
最简单的用法,快速和脏代码
var sl1,sl2,sl3:TStrings;
sl1:=MakeList;
Run Code Online (Sandbox Code Playgroud)
当程序员想要更明确的所有权和/或使用自定义类型时
sl2:=MakeList(TMyStringsList.create);
Run Code Online (Sandbox Code Playgroud)
何时创建对象
sl3:=TMyStringList.Create;
....
MakeList(sl3);
Run Code Online (Sandbox Code Playgroud)