2010-12-08 1 views
4

TInterfacedObject 파생 클래스의 인스턴스가 소멸되고 누가 소멸자를 호출하는지 궁금합니다. ScopedLock 클래스를 작성했습니다. ScopedLock 클래스는 인스턴스가 범위를 벗어날 때 자동으로 동기화 메서드의 메서드를 호출해야합니다. C++에서 알려진 RAII 개념이지만 잠금 인스턴스가 범위를 벗어날 때 소멸자가 호출되도록 보장되는지 여부는 알 수 없습니다.TInterfacedObject.Destroy (ScopedLock 클래스)가 호출 된시기

ILock = interface 
end; 

ScopedLock<T: TSynchroObject> = class(TInterfacedObject, ILock) 
strict private 
    sync_ : T; 
public 
    constructor Create(synchro : T); reintroduce; 
    destructor Destroy;override; 
end; 

implementation 
{ ScopedLock<T> } 

constructor ScopedLock<T>.Create(synchro: T); 
begin 
    inherited Create;; 
    sync_ := synchro; 
    sync_.Acquire; 
end; 

destructor ScopedLock<T>.Destroy; 
begin 
    sync_.Release; 
    inherited; 
end; 

{ Example } 
function Example.Foo: Integer; 
var 
    lock : ILock; 
begin 
    lock := ScopedLock<TCriticalSection>.Create(mySync); 
    // ... 
end; // mySync released ? 

간단한 테스트 케이스에서는 문제가 없지만 안전합니까?

답변

5

예, 저장됩니다. 귀하의 코드

function Example.Foo: Integer; 
var 
    lock : ILock; 
begin 
    lock := ScopedLock<TCriticalSection>.Create(mySync); 
    // ... 
end; 

당신은 잠금 VAR이 범위를 벗어나면 그 심판이 카운트가 감소되는 것을 볼 수있는 다음 의사

function Example.Foo: Integer; 
var 
    lock : ILock; 
begin 
    lock := ScopedLock<TCriticalSection>.Create(mySync); 
    lock._AddRef; // ref count = 1 
    try 
// .. 
    finally 
    lock._Release; // ref count = 0, free lock object 
    end; 

로 컴파일되고, 제로가되었다 및 잠금 개체는 자동으로 소멸됩니다.

6

유일한 문제는 함수가 인라인 된 경우입니다. ILock 참조를 포함하는 로컬 변수는 인라인 된 함수를 호출하는 함수의 범위로 승격됩니다. 자물쇠가 원하는 길이보다 오래 살 수 있습니다.

반면에 인터페이스 참조를 반환하는 함수 (예 : Create라는 클래스 함수)를 작성할 경우 (객체 참조와 반대) 변수를 실제로 선언 할 필요가 없습니다. 컴파일러는 반환 값을 받기 위해 숨겨진 로컬을 만듭니다 (결과 변수를 전달하여 인터페이스 및 문자열 같은 모든 관리되는 유형이 실제로 반환 됨). 이 숨겨진 로컬은 명시 적 로컬처럼 작동합니다.

나는 여기에 대한 자세한 내용을 썼다 : 나는 좋은 오래된 시도/마지막으로보다 실제로 더 정확한 동안 접근 당신이 신봉한다는 표시되지 않습니다 http://blog.barrkel.com/2010/01/one-liner-raii-in-delphi.html

+1

좋은 기사 .. 델파이 컴파일러가 어떻게 함수를 인라인하기로 결정했는지 말해 줄 수 있습니까? 인라인을 방지/시행 할 수있는 방법이 있습니까? – hansmaad

+0

@hansmaad : {$ INLINE AUTO}가 활성화되어 있지 않으면 인라인 명령으로 표시되지 않은 함수를 자동으로 인라인하지 않습니다. 그러나 $ INLINE AUTO는 매우 똑똑한 휴리스틱을 사용하지 않습니다. 이는 특정 크기 이하의 모든 함수를 인라인 할 것이고 쉽게 크기 폭발을 일으킬 수 있습니다. 즉, $ INLINE AUTO가 적용되지 않을 가능성이 있습니다. 따라서 프로 시저 헤더의 끝에있는 'inline'지시문을 사용하여 인라이닝 할 함수를 직접 표시하면 걱정할 필요가 있습니다. –

0

. 명확하고 명확한 솔루션을 선택하여 애매하고 불투명 한 솔루션으로 대체했습니다.

실제 작업 Delphi 코드는 Try/Finally로 가득하므로 자연스러운 관용구 여야합니다. 나는 글의 단점을 볼 수 없다 :

mySync.Acquire; 
Try 
    //do stuff 
Finally 
    mySync.Release; 
End; 
+0

코드가 적기 때문에? 또는, 스코프가 생성 된 시점에서 스코프를 명시 적으로 명확하게하기 때문입니다. 아마 다른 이유도 있습니다. try/finally는 나쁘지는 않지만 다른 기술도 잘 될 수 있습니다. –

+0

@David M 읽고 쓰고 파싱하는 데 익숙해 져야합니다. 어쨌든 어쨌든 Try/Finally는 작업을 수행 할 때마다 사용할 수 있습니다. 리소스를 보호하기위한 여러 가지 방법 (누수 또는 직렬화)이 내 마음에 어쨌든 이루어지면 코드를 읽고 검사 할 때 추가적인 복잡성이 발생합니다. 나는 "여러 가지"의 Perl 학교와 반대로 Python 학교에서 "일을하는 한 가지 방법"이라고 생각합니다 !! –

+4

@Daivd H : "일을하는 한 가지 방법"은 가능할 때 RAII 기술을 사용하는 것입니다.나는 그것으로 잘못 갈 수 없다. 필자가 직렬화를 취득 할 때마다 코드의 추가 라인을 작성하지 않고 조만간 정확한 위치에 릴리스 할 것이라고 확신한다. – hansmaad

관련 문제