将记录复制到动态分配的内存时,Delphi 7引用计数错误

mal*_*lom 4 delphi automatic-ref-counting

在将带有托管字符串字段的记录类型变量分配给动态分配的缓冲区时,我在Delphi中遇到了一种奇怪的行为.这有什么问题,如何纠正?

type
  PRec = ^TRec;
  TRec = packed record
    Foo: integer;
    Bar: string;
  end;

procedure Error;
var
  P, Q: PRec;
  R, T: TRec;
begin
  R.Foo := 1;
  R.Bar := 'Bar';
  T := R; // Ready
  Q := @T;
  Q^ := R; // Ready
  GetMem(P, SizeOf(TRec));
  P^ := R; // Access violation in _LStrAsg at 
           // "MOV     ECX,[EDX-skew].StrRec.refCnt"

  R := P^; // Just to keep reference while debugging
end;
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 6

您的记录是托管记录.因此,它需要初始化.您的代码使用的GetMem不会初始化记录.相反,你应该使用New.更换

GetMem(P, SizeOf(TRec));
Run Code Online (Sandbox Code Playgroud)

New(P);
Run Code Online (Sandbox Code Playgroud)

同样,当您需要解除分配时,您必须完成记录.使用Dispose而不是FreeMem.

如果出于某种原因需要这样做,则可以手动初始化和完成.这看起来像这样:

// allocate and initialize
GetMem(P, SizeOf(P^));
Initialize(P^);

// finalize and deallocate
Finalize(P^);
FreeMem(P);
Run Code Online (Sandbox Code Playgroud)

  • 应该注意,在这种情况下,必须仅仅因为记录包含字符串而初始化指针.`Initialize(P ^)`可以安全地用来代替`FillChar(P ^,SizeOf(P ^),0)`,但有一点不同:*如果调用Finalize中指定的变量不包含长字符串,变体或者接口,编译器消除了调用并且没有生成代码.*参见[System.Initialize](http://docwiki.embarcadero.com/Libraries/XE4/en/System.Initialize) (2认同)