除了这个问题,我还对docwiki进行了一些测试和研究.我的结论是这种代码应该没有内存泄漏:
function testResultObject: TClassA;
begin
Result := TClassA.Create;
Result.DoSomething;
end;
Run Code Online (Sandbox Code Playgroud)
然后我可以用这种方式调用上面的代码:
var k: TClassA;
begin
k := testResultObject;
try
//code code code
finally
k.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud)
正如雷米在答案中建议的那样,最好避免这种做事方式,而是使用类似的东西testResultObject(x: TClassA): boolean
.在这种情况下,返回true/false可以告诉我一切是否正常并且我正在传递已经创建的对象.
看看这段代码:
function testResultObject: TClassA;
begin
Result := TClassA.Create;
try
Result.DoSomething;
except
Result.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud)
上面第一个版本的函数的问题是DoSomething
可能引发异常,如果是这样,我会泄漏内存.第二个实现try-except
可以解决吗?以后我必须检查结果是否已分配或为零.
我同意(如上所述)testResultObject(x: TClassA): boolean
会更好.我只是想知道返回类功能的方式是否可以像我写的那样修复.
您的代码存在严重问题.如果出现错误,它会吞下异常,并返回无效的对象引用.
这很容易解决.规范方式如下:
function testResultObject: TClassA;
begin
Result := TClassA.Create;
try
Result.DoSomething;
except
Result.Free;
raise;
end;
end;
Run Code Online (Sandbox Code Playgroud)
函数成功并返回一个新对象.或者它失败了,自行清理,并引发异常.
换句话说,此函数的外观和行为就像构造函数一样.您以相同的方式使用它:
obj := testResultObject;
try
// do things with obj
finally
obj.Free;
end;
Run Code Online (Sandbox Code Playgroud)
你的第二种方法有效,但有两个严重的问题.
我建议您对第二种方法进行以下改进:
{Name has a clue that caller should take ownership of a new object returned}
function CreateObjectA: TClassA;
begin
{Once object is successfully created, internal resource protection is required:
- if no error, it is callers responsibility to destroy the returned object
- if error, caller must assume creation *failed* so must destroy object here
Also, by assigning Result of successful Create before *try*:
The object (reference) is returned
**if-and-only-if**
This function returns 'normally' (i.e. no exception state)}
Result := TClassA.Create;
try
Result.DoSomething; {that could fail}
except
{Cleanup only if something goes wrong:
caller should not be responsible for errors *within* this method}
Result.Free;
{Re-raise the exception to notify caller:
exception state means caller does not "receive" Result...
code jumps to next finally or except block}
raise;
end;
end;
Run Code Online (Sandbox Code Playgroud)
上述create函数最重要的好处是:就任何调用者/客户端代码而言,它的行为与普通的TObject.Create完全相同.
因此正确的使用模式完全相同.
请注意,我并不热衷于J的FreeAndNil
建议,因为如果调用代码不检查结果是否已分配:它很可能是AV.并且正确检查结果的代码会有点混乱:
var k: TClassA;
begin
k := testResultObject; {assuming nil result on failed create, next/similar is *required*}
if Assigned(k) then {Note how this differs from normal try finally pattern}
try
//code using k
finally
k.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud)
注意:重要的是要注意,你不能让你的来电者忽略内存管理; 这让我进入下一节.
除此之外,如果你testResultObject
需要一个输入对象,你需要调用者根据需要创建和管理它的生命周期,那么制造粗心错误的可能性要小得多.我不确定你为什么这么拒绝这种方法?如果不采用不同的内存模型,就不能比以下更简单.
var k: TClassA;
begin
k := TClassA.Create;
try
testResultObject(k); {Where this is simply implemented as k.DoSomething;}
//more code using k
finally
k.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
854 次 |
最近记录: |