我问another question关于使用运营商的记录。在测试期间,我发现了一个异常现象,这种类型的两个实例似乎共享相同的内存。
该记录有一个Integer数组......
type
TVersion = record
Values: array of Integer;
function Count: Integer;
class operator implicit(aVersion: TVersion): String;
class operator implicit(aVersion: String): TVersion;
end;
class operator TVersion.implicit(aVersion: TVersion): String;
var
X: Integer;
begin
Result:= '';
for X := 0 to Length(aVersion.Values)-1 do begin
if X > 0 then Result:= Result + '.';
Result:= Result + IntToStr(aVersion.Values[X]);
end;
end;
class operator TVersion.implicit(aVersion: String): TVersion;
var
S, T: String;
I: Integer;
begin
S:= aVersion + '.';
SetLength(Result.Values, 0);
while Length(S) > 0 do begin
I:= Pos('.', S);
T:= Copy(S, 1, I-1);
Delete(S, 1, I);
SetLength(Result.Values, Length(Result.Values)+1);
Result.Values[Length(Result.Values)-1]:= StrToIntDef(T, 0);
end;
end;
function TVersion.Count: Integer;
begin
Result:= Length(Values);
end;
现在我尝试实现这个......
var
V1, V2: TVersion;
begin
V1:= '1.2.3.4';
V2:= V1;
ShowMessage(V1);
ShowMessage(V2);
V2.Values[2]:= 8;
ShowMessage(V1);
ShowMessage(V2);
end;
我希望V2
1.2.8.4
和V1
保持1.2.3.4
。但是,V1
也会更改为1.2.8.4
。
如何在分配这些记录时保持这些记录的独立性?
答案 0 :(得分:4)
此行为是设计使然。动态数组变量是指针。分配动态数组变量时,需要复制指针并增加引用计数。当然,当您指定包含动态数组的复合结构时,也会发生这种情况。
documentation涵盖了这个:
如果X和Y是相同动态数组类型的变量,则X:= Y将X指向与Y相同的数组。(在执行此操作之前无需为X分配内存。)与字符串和静态不同数组,写时复制不用于动态数组,因此它们在写入之前不会自动复制。
为了得到一个心理模型,动态数组的这个引用的方式与类和接口相同。这与简单类型(整数,双重等),字符串,记录等形成对比。
处理此问题的标准方法如下:
最后一步是棘手的部分。通过调用SetLength
:
SetLength(arr, Length(arr));
SetLength
所做的一个承诺是它总是使它的第一个参数是唯一的。
这具有实现写时复制的效果。据我所知,不可能实现copy-on-assign,因为编译器不会为赋值运算符提供挂钩。
答案是:
如何在分配这些记录时保持这些记录的独立性?
是你不能。您需要使用copy-on-write。