首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么这个字符串的引用计数是4?(Delphi 2007)

为什么这个字符串的引用计数是4?(Delphi 2007)
EN

Stack Overflow用户
提问于 2011-06-26 17:51:26
回答 1查看 1.5K关注 0票数 16

这是一个非常Delphi特有的问题(甚至可能是Delphi 2007特有的)。我目前正在编写一个简单的StringPool类来存储字符串。作为一个优秀的小程序员,我还添加了单元测试,并发现了一些令我困惑的东西。

这是实习生的代码:

代码语言:javascript
复制
function TStringPool.Intern(const _s: string): string;
var
  Idx: Integer;
begin
  if FList.Find(_s, Idx) then
    Result := FList[Idx]
  else begin
    Result := _s;
    if FMakeStringsUnique then
      UniqueString(Result);
    FList.Add(Result);
  end;
end;

没什么特别的: FList是一个排序的TStringList,所以代码所做的就是在列表中查找字符串,如果它已经存在,则返回现有的字符串。如果它还不在列表中,它将首先调用UniqueString以确保引用计数为1,然后将其添加到列表中。(我检查了Result的引用计数,添加两次'hallo‘后,引用计数为3,不出所料。)

现在来看测试代码:

代码语言:javascript
复制
procedure TestStringPool.TestUnique;
var
  s1: string;
  s2: string;
begin
  s1 := FPool.Intern('hallo');
  CheckEquals(2, GetStringReferenceCount(s1));
  s2 := s1;
  CheckEquals(3, GetStringReferenceCount(s1));
  CheckEquals(3, GetStringReferenceCount(s2));
  UniqueString(s2);
  CheckEquals(1, GetStringReferenceCount(s2));
  s2 := FPool.Intern(s2);
  CheckEquals(Integer(Pointer(s1)), Integer(Pointer(s2)));
  CheckEquals(3, GetStringReferenceCount(s2));
end;

这会将字符串'hallo‘添加到字符串池中两次,并检查字符串的引用计数以及s1和s2是否确实指向相同的字符串描述符。

除了最后一个之外,每个CheckEquals都像预期的那样工作。它会失败,并显示错误"expected:<3> but was:<4>“。

那么,为什么这里的引用计数是4?我预计会有3个:

  • s1
  • s2
  • and StringList

中的另一个

这是Delphi2007,因此字符串是AnsiStrings。

哦,是的,函数StringReferenceCount是这样实现的:

代码语言:javascript
复制
function GetStringReferenceCount(const _s: AnsiString): integer;
var
  ptr: PLongWord;
begin
  ptr := Pointer(_s);
  if ptr = nil then begin
    // special case: Empty strings are represented by NIL pointers
    Result := MaxInt;
  end else begin
    // The string descriptor contains the following two longwords:
    // Offset -1: Length
    // Offset -2: Reference count
    Dec(Ptr, 2);
    Result := ptr^;
  end;
end;

在调试器中,可以将其计算为:

代码语言:javascript
复制
plongword(integer(pointer(s2))-8)^

Serg给出的答案(看起来是100%正确)加上了

如果我替换掉

代码语言:javascript
复制
s2 := FPool.Intern(s2);

使用

代码语言:javascript
复制
s3 := FPool.Intern(s2);
s2 := '';

然后检查s3 (和s1)的引用计数,不出所料是3。正是因为将FPool.Intern (s2 )的结果再次赋值给s2 (s2既是一个参数,也是函数结果的目标),才导致了这种现象。Delphi引入了一个隐藏的字符串变量来分配结果。

另外,如果我将函数更改为一个过程:

代码语言:javascript
复制
procedure TStringPool.Intern(var _s: string);

引用计数如预期的那样为3,因为不需要任何隐藏变量。

如果有人对这个TStringPool实现感兴趣:它在MPL下是开源的,可以作为dzlib的一部分获得,而dzlib又是dzchart的一部分:

https://sourceforge.net/p/dzlib/code/HEAD/tree/dzlib/trunk/src/u_dzStringPool.pas

但如上所述:这并不完全是火箭科学。;-)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-06-26 20:34:35

测试一下:

代码语言:javascript
复制
function RefCount(const _s: AnsiString): integer;
var
  ptr: PLongWord;
begin
  ptr := Pointer(_s);
  Dec(Ptr, 2);
  Result := ptr^;
end;

function Add(const S: string): string;
begin
  Result:= S;
end;

procedure TForm9.Button1Click(Sender: TObject);
var
  s1: string;
  s2: string;

begin
  s1:= 'Hello';
  UniqueString(s1);
  s2:= s1;
  ShowMessage(Format('%d', [RefCount(s1)]));   // 2
  s2:= Add(s1);
  ShowMessage(Format('%d', [RefCount(s1)]));   // 2
  s1:= Add(s1);
  ShowMessage(Format('%d', [RefCount(s1)]));   // 3
end;

如果编写s1:= Add(s1),编译器会创建一个隐藏的本地字符串变量,该变量负责递增引用计数。你不应该为这事操心。

票数 14
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6483257

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档