16

Bu çok Delphi'ye özgü bir sorudur (belki de Delphi 2007'ye özgü). Şu anda dizeleri tutmak için basit bir StringPool sınıfı yazıyorum. İyi bir küçük kodlayıcı olarak ben de ünite testleri ekledim ve beni şaşırtan bir şey buldum. Zaten varsa FList sıralanır bir TStringList, bu nedenle tüm kod listesinde dizeyi bakıyor yapar ve: gerçekten fanteziBu dize neden referans sayısı 4? (Delphi 2007)

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; 

Hiçbir şey:

Bu

staj kodudur varolan dizeyi döndürür. Henüz listede bulunmuyorsa, önce 1 referans sayısını sağlamak ve daha sonra listeye eklemek için UniqueString'i çağırır. (Ben Sonuç başvuru sayısı kontrol ve beklendiği gibi 'merhaba', iki kez eklendikten sonra 3 içindir.)

Şimdi test koduna:

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; 

Bu dizeyi 'merhaba' ekler dize havuzu iki kez ve dizenin referans sayısını ve ayrıca s1 ve s2'nin aynı dize tanımlayıcısını işaret ettiğini kontrol eder.

Her CheckEquals beklendiği gibi çalışır ancak sonuncudur. "Beklenen: < 3> hatasıyla başarısız olur, ancak: < 4>" idi.

Peki, neden referans sayısı 4 burada? Ben 3 beklerdik:

  • s1
  • s2
  • ve StringList

başka bir Bu Delphi 2007 ve dizeleri nedenle AnsiStrings vardır.

plongword(integer(pointer(s2))-8)^ 

Sadece görünüyor (Serg gelen cevaba eklemek için: Aynı olarak değerlendirilebilir ayıklayıcıya olarak

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; 

: olarak

Ah evet, fonksiyon StringReferenceCount uygulanır % 100 doğru olması için):

Daha sonra

s3 := FPool.Intern(s2); 
s2 := ''; 

ile

s2 := FPool.Intern(s2); 

s3 referans sayısını kontrol (ve s 1), beklendiği gibi bu 3'tür. Bu, FPool.Intern (s2) sonucunu tekrar s2'ye atamaktan (s2, hem parametre hem de işlev sonucunun hedefi) bu fenomene neden oluyor. Delphi, sonucu atamak için gizli bir dize değişkeni sunar.

Ayrıca, bir prosedüre işlevini değiştirirseniz:

procedure TStringPool.Intern(var _s: string); 

başvuru sayısı hiçbir gizli değişken gereklidir çünkü beklendiği gibi 3'tür.vaka kimseye yılında


bu TStringPool uygulanmasında ilgilenmektedir: https://sourceforge.net/p/dzlib/code/HEAD/tree/dzlib/trunk/src/u_dzStringPool.pas

Ama

söyledi: O da dzchart parçası olan dzlib, bir parçası olarak açık MPL altında kaynak ve kullanılabilir yukarıda: Tam olarak roket bilimi değildir. ;-)

+0

TestUnique'in sonunda S1 için ref sayısını kontrol edebilir misiniz? Ref sayısının bu noktada ne olduğunu merak ediyorum. –

+0

kesinlikle tahmincilerden saçmalık almak için debug dcus –

+0

+ kullanabilirsiniz. –

cevap

14

Testi bu: Eğer s1:= Add(s1) yazarsanız

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; 

derleyici bir gizli yerel dize değişkeni oluşturur ve bu değişken ref sayısını artırarak sorumludur. Bu konuda rahatsız etmemelisin.

+1

Yani bu temel olarak Delphi fonksiyon sonuçlarının VAR parametreleri olarak geçirilen bir eseri mi? Arayüzler ile benzer bir etkiyi biliyordum (ki bu da bazen denemek için kullanılıyordu. Nihayetinde kurgular) ama bir şekilde dizelere de uygulandığını düşünmüyordu. – dummzeuch

+1

@dummzeuch - Bence bu doğru. Derleyici, s1'i "var" olarak "s1: = Add (s1)" olarak geçemez, böylece bir gizli değişken oluşturur, bunu "var" olarak geçirir ve s1'e atar (artan sayı sayısı) – kludg