2013-06-10 6 views
2

동적 배열 필드가있는 advanced record이 있습니다. 레코드는 레코드와 바이트의 연결을 위해 class operator입니다. 또한 바이트를 추가하는 Add 메서드입니다.레코드의 동적 배열 참조 횟수

레코드를 사용하려면 동적 배열 필드의 참조 횟수가 중요합니다. 아래의 두 시험 절차를 실행하면 볼 수 있습니다 1.

program TestReferenceCount; 

{$APPTYPE CONSOLE} 

uses 
    System.SysUtils; 

Type 
    TRec = record 
    class operator Add(const a: TRec; b: Byte): TRec; 
    private type 
    PDynArrayRec = ^TDynArrayRec; 
    TDynArrayRec = packed record 
     {$IFDEF CPUX64} 
     _Padding: LongInt; // Make 16 byte align for payload.. 
     {$ENDIF} 
     RefCnt: LongInt; 
     Length: NativeInt; 
    end; 
    private 
    FArr: TBytes; 
    function GetRefCnt: Integer; 
    public 
    procedure Add(b : Byte); 
    property RefCnt: Integer read GetRefCnt; 
    end; 

procedure TRec.Add(b : Byte); 
var 
    prevLen: Integer; 
begin 
    prevLen := System.Length(Self.FArr); 
    SetLength(Self.FArr, prevLen + 1); 
    Self.FArr[prevLen] := b; 
end; 

class operator TRec.Add(const a: TRec; b: Byte): TRec; 
var 
    aLen: Integer; 
begin 
    aLen := System.Length(a.FArr); 
    SetLength(Result.FArr, aLen + 1); 
    System.Move(a.FArr[0], Result.FArr[0], aLen); 
    Result.FArr[aLen] := b; 
end; 

function TRec.GetRefCnt: Integer; 
begin 
    if Assigned(FArr) then 
    Result := PDynArrayRec(NativeInt(FArr) - SizeOf(TDynArrayRec)).RefCnt 
    else 
    Result := 0; 
end; 

procedure TestConcatenation; 
var 
    r1 : TRec; 
begin 
    WriteLn('RC:', r1.RefCnt); // <-- Writes 0 
    r1 := r1 + 65; 
    WriteLn('RC:', r1.RefCnt); // <-- Writes 2 
end; 

procedure TestAdd; 
var 
    r1 : TRec; 
begin 
    WriteLn('RC:', r1.RefCnt); // <-- Writes 0 
    r1.Add(65); 
    WriteLn('RC:', r1.RefCnt); // <-- Writes 1 
end; 

begin 
    TestConcatenation; 
    TestAdd; 
    ReadLn; 
end. 

컴파일러의 참조 카운트에 추가 방법의 결과는 별도의 참조 카운트 때을 담당하면서 2의 참조 카운트에 연결 결과 레코드 변수는 범위를 벗어나므로이 시점에서 실제로 아무런 문제가 없습니다.

하지만이 동작을 설명 할 수 있습니까? 문서화되지 않은 구현 세부 사항입니까? 추가 카운트를 피할 수있는 방법이 있습니까?

+0

문서화되지 않은 구현 세부 사항에 대해 말하자면 동적 배열의 참조 카운트에 의존하여 처음부터 특정 값을 갖게되는 것이 취성 설계처럼 보입니다. –

+0

@RobKennedy, 무슨 뜻인지 잘 모르겠지만 왜 동적 배열의 참조 카운트를 신뢰하지 않겠습니까? 문자열과 마찬가지로 개수가 0이되면 힙에서 자체를 제거합니다. –

+0

물론, 그것이 0 일 때 누가 말합니까? 이 설명서에는 참조 횟수가 언제 변경되는지에 대한 철저한 목록이 없습니다. 참조 횟수가 1 일 때만, 0으로 변경되면 어떻게되는지 알려줍니다. 그것은 * 언제 * 그것이 0으로 변한다고 말하지 않습니다. –

답변

2

은의이 기능을 살펴 보자 :

procedure TestConcatenation; 
var 
    r1 : TRec; 
begin 
    r1 := r1 + 65; 
end; 

컴파일러는 실제로 이런 식으로 구현 :

procedure TestConcatenation; 
var 
    r1 : TRec; 
    tmp : TRec; 
begin 
    tmp := r1 + 65; 
    r1 := tmp; 
end; 

컴파일러는 r1 + 65의 결과를 저장하기 위해 임시 지역을 소개합니다. 그럴만한 이유가 있습니다. 그렇지 않은 경우 추가 연산자의 결과를 어디에 쓸 것입니까? 최종 목적지가 r1이므로 추가 연산자가 r1에 직접 쓰면 입력 변수가 수정됩니다.

임시 로컬을 생성하는 컴파일러를 중지 할 방법이 없습니다.

+1

'... 당신의 adition 연산자가 r1에 직접 쓰면 입력 값이 수정됩니다 .' 그게 다야! 고마워. 고마워. 나는 asm 코드를 볼 수 있었지만 이유를 이해할 수 없었다. –