Joh*_*ica 3 delphi string copy-on-write
我有一个记录,看到这个问题的背景信息.
TDigits = AnsiString; //Should be `= array of NativeUInt`, but string has COW
TBigint = record
Digit: TDigits; // Unsigned number, LSB stored in D[0], MSB in D[size-1]
Size: Byte; // Mininum = 4, maximum 127.
MSI: Byte; // Most significant (native)integer minimum=1, maximum=127
Sign: Shortint;
class operator Implicit(a: Integer): TBigint;
Run Code Online (Sandbox Code Playgroud)
背景
我正在使用一个(几乎)像普通整数一样工作的bignum类.
因此a:= 1000000*10000000*12000000*10000000*1000000;产生非常有用的结果.为此,我使用了一个记录class operators.这些触发自动类型转换和初始化.
除非没有转换,因为我正在分配TBigint给另一个TBigint.
解决方案
使用Ansistring存储核心数据,它具有写入时复制功能,并在需要时自行克隆.
问题是:(如果Delphi不知道你正在改变字符串,COW不起作用)
我有一些纯粹的汇编程序例程来操纵数字dynamic array伪装成Ansistring.
但是,当我做这样的事情时:
Label1.Caption:= BigintToStr(b);
..... this fires:
function BigintToStr(const X: TBigint): AnsiString;
var
..
LocX:= x; <<-- assignment, locX and X are joined at the hip.
repeat
D := DivBigint(LocX, 1000000000, LocX); <<-- this routine changes LocX
^^+-- but assembler routines bypass COW
Run Code Online (Sandbox Code Playgroud)
X并且LocX是在臀部,无论发生什么事,以一个发生在其他关节.
很明显,Delphi不知道asm例程DivBigint正在改变LocX,因此COW是有序的.
解决方法
如果我将例程更改为:
function BigintToStr(const X: TBigint): AnsiString;
var
..
LocX:= x;
LocX.Digit[2]:= #0; <<-- inconsequential change to force COW.
repeat
D := DivBigint(LocX, 1000000000, LocX);
Run Code Online (Sandbox Code Playgroud)
德尔福得到了全面的解决,表现得很好.
LocX并且X没有关联,一切正常.
但是我不想在一些空白的空间中做出愚蠢的改变.
有没有一个体面/适当/官方的方法来强制在字符串中触发COW?
或许类似于系统调用?
*圈出您最喜欢的选项(带有一个手绘圈)
应该是评论,但需要更多空间......
如果你需要打电话UniqueString或等同.
您也可以保留动态记录.
手册中的引用:
在调用SetLength之后,S保证引用唯一的字符串或数组 - 即引用计数为1的字符串或数组.如果没有足够的可用内存来重新分配变量,则SetLength会引发EOutOfMemory异常.
请注意,此行为甚至适用于调用时SetLength(Length(myArray));.
德尔福将为您制作副本并解决问题.
所以事实证明,不需要使用AnsiStrings复杂化毕竟,只要你在接受记录作为var参数的每个方法中调用SetLength .
优点
这有一个额外的好处,如果你调用SetLength扩展数组(经常发生),添加的空间将被零初始化.AnsiString不会发生任何事情.
此外,您无需为大小翻译而烦恼,因为您array of TXYZ已经知道其元素的大小.使用AnsiString时,您需要添加* SizeOf(somestruct)所有地方.
不需要进行类型转换,简化了代码; 在调试器中,数据按设计显示.

如您所见,这两个实例不再链接.