C++/CLI의 Dispose Pattern에 대한 고찰

리소스 해제를 위한 .NET 개발환경에서 제공하는 Dispose 패턴을 파악하기 위해 테스트용으로 적용할 클래스 정의는 다음과 같으며 총 세가지의 경우로 시험을 해보았다.

ref class T : public IDisposable {
public:
    T() {
        Console::WriteLine(L"T() invoked");
    }

    ~T() {
        Console::WriteLine(L"~T() invoked");
    }

    !T() {
        Console::WriteLine(L"!T() invoked");
    }
};

첫번째 시험 코드는 마치 지역변수처럼 할당하는 경우이다. 하지만 절대 지역 변수가 아니라는 점.. CLR Heap에 할당된다.

int main(array ^args)
{
    T a;
    return 0;
}

실행 결과는 다음과 같다. 지역변수처럼 변수의 유효 Scope를 벗어나는 순간 소멸자가 호출되었다. 실제 IL에 의해 구현된 내부 흐름은 소멸자 호출이 아니라 IDisposable::Dispose 매서드의 호출이다. 즉, 소멸자가 IDisposable::Dispose의 재구현이다. 또한 내부적으로 GC에 의해 호출되어져야했을 Finalize 매서드는 호출되지 않게 조치된다. C#과는 다르게 Finalize가 호출되지 않도록 자동화되었다는 점이 매우 특이하다.

T() invoked
~T() invoked

두번째 시험 코드는 C++에서는 포인터 개념으로 생각되는, 즉 C++/CLI는 Handle 개념으로 CLR의 Heap에 객체를 생성하였고 delete를 호출하지 않은 경우이다.

int main(array ^args)
{
    T ^a = gcnew T();
    return 0;
}

실행 결과는 다음과 같다. Finalize에 해당하는 !T()가 호출되었다는 점에 유의하자.

T() invoked
!T() invoked

세번째 시험 코드는 두번째와 다르게 delete 연산자를 적용해 주었다.

int main(array ^args)
{
    T ^a = gcnew T();
    delete a;
    return 0;
}

실행 결과는 다음과 같다. Finalize가 아닌 Dispose가 호출되었다.

T() invoked
~T() invoked

여기서 얻을 수 있는 가장 중요한 한가지 결론은 ~T()에 해당하는 Dispose()와 !T()에 해당하는 Finalize()의 코드는 절대로 같이 호출되지 않는다는 점이다. C++/CLI의 사용자가 .NET의 참조형 변수를 지역변수처럼 사용하든지, gcnew에 의해 할당하여 사용하든지.. 또한 사용한 후 delete를 했든지, 하지 않았든지 간에 ~T()와 !T() 둘중에 하나는 반드시 실행되다는 점이다. ~T()는 기존의 C++ 개념으로써 호출되며 !T()는 .NET의 GC에 의해 호출된다. 즉, 리소스 해제를 위한 코드는 ~T()와 !T()에 똑 같이 중복적으로 와야한다고 생각한다.

“C++/CLI의 Dispose Pattern에 대한 고찰”에 대한 7개의 댓글

  1. arload님, 댓글 감사합니다~ 블로그에 잠시 방문해 봤습니다. 아키텍쳐가 주토픽인듯 싶습니다. 생각날때 종종 방문하겠습니다~ ^^

  2. C++/CLI에서
    1. 스택되감기에 의한 해제는 Dispose() 가 호출 = ~T()
    2. delete 에 의한 해제는 Dispose() 가 호출 = ~T()
    3. GC에 의한 해제는 Finalize()가 호출 = !T()

    잘 보고 갑니다.
    그러면, !T()가 없으면, ~T()가 호출 되는건가요?

  3. 잘 읽었습니다… 좀 헷갈려서 저도 테스트를 해 봤습니다. 그리고 추가적인 실험결과를 공유합니다.

    Native C++ 에서는 상속관계에 있어서 부모의 소멸자를 호출하기 위해서는 “~” 소멸자에 virtual 키워드를 붙여야 합니다. 하지만 C++/CLI 의 ref class 는 “~” 든 “!” 든 virtual 키워드를 붙이지 않아도 되더군요. 상속구조에 맞게 알아서 소멸자를 불러 줍니다( “!” 는 아예 virtual 을 지정할 수도 없습니다 ).

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다