我有记录类型:
type
TIPInfo = record
IP,
HostName,
City,
Region,
Country,
Loc,
Org: WideString
end;
返回记录数据的功能:
function GetPublicIPInfo(var IPInfo: TIPInfo): Boolean;
begin
// initialize
FillChar(IPInfo, SizeOf(TIPInfo), 0);
// populate data
IPInfo.IP := GetVallue('ip');
IPInfo.HostName := GetVallue('hostname');
IPInfo.City := GetVallue('city');
// etc...
Result := IsOk;
end;
来电者:
var
IPInfo: TIPInfo;
if GetPublicIPInfo(IPInfo) then... // use data
通过调用var TIPInfo
来初始化FillChar
是正确的方法还是应该将每个字段设置为空字符串?来电者应该这样做吗?
另外,在案例中使用out
参数会更好吗(因为函数没有读取数据)?
答案 0 :(得分:5)
仅使用FillChar
是错误的。如果任何WideString
成员不为空,那么您将以这种方式泄露它们。相反,我建议如下:
Finalize(IPInfo);
FillChar(IPInfo, SizeOf(TIPInfo), 0);
或者另一种方法是将默认记录定义为类型化常量:
const
DefaultIPInfo: TIPInfo = ();
然后你可以使用简单的作业:
IPInfo := DefaultIPInfo;
在现代版本的Delphi中,您可以使用这个更易读的代码:
IPInfo := Default(TIPInfo);
有关此特定主题的更多信息,请参阅以下主题:
请注意,代码中的泄漏很难找到,因为WideString
变量实现为COM BSTR
对象,并在COM堆上分配。因此,如果对Delphi内存管理器使用内存泄漏检测,则不会检测到泄漏,因为它是从另一个堆泄漏的。
在您的情况下,由于您的记录是托管类型,并且仅包含托管类型,因此您可以使用out
参数来实现良好效果。对于托管类型,out
参数表示编译器将在调用站点生成代码,以便在传递记录之前对其进行默认初始化。
考虑以下计划:
{$APPTYPE CONSOLE}
type
TRec = record
Value: WideString;
end;
procedure Foo1(var rec: TRec);
begin
end;
procedure Foo2(out rec: TRec);
begin
end;
procedure Main;
var
rec: TRec;
begin
rec.Value := 'Foo';
Foo1(rec);
Writeln(rec.Value);
Foo2(rec);
Writeln(rec.Value);
end;
begin
Main;
end.
输出结果为:
Foo
如果您的记录包含托管和非托管类型的混合,那么情况就不那么好了。
{$APPTYPE CONSOLE}
type
TRec = record
Value1: WideString;
Value2: Integer;
end;
procedure Foo1(var rec: TRec);
begin
end;
procedure Foo2(out rec: TRec);
begin
end;
procedure Main;
var
rec: TRec;
begin
rec.Value1 := 'Foo';
rec.Value2 := 42;
Foo1(rec);
Writeln(rec.Value1);
Writeln(rec.Value2);
Foo2(rec);
Writeln(rec.Value1);
Writeln(rec.Value2);
end;
begin
Main;
end.
输出结果为:
Foo 42 42
仅为out
参数默认初始化托管成员。因此,最好的办法是默认自己初始化变量,即使它是作为out
参数传递的。
有关out
参数的更多信息,请访问:What's the difference between "var" and "out" parameters?