Windows 10 UWP에서 C++/CX의 ref class객체들을 STL컨테이너와 함께 사용할 경우 ref count관리는 어떻게 이루어지는가

나는 Windows 10 UWP앱을 C++/CX로 개발하고 있다.
그런데 난 STL을 잘 사용하지 않는다. 아주 가끔 사용한다. 정말 노가다가 귀찮을때만 쓴다.
오늘 UWP앱으로 테스트할게 있어서 몇 줄 코드를 작성했다.
노가다가 귀찮아서 stl::queue를 사용했다. 무심코 ref new로 할당한 ref class 객체(이하 ^객체-hat object로 표시함)를 여기 집어넣으면 ref count가 증가할까? 라는 의문이 들었다. 테스트를 해보니 놀랍게도 ref count 자동으로 관리되었다. queue에 push하면 ref count 1증가, pop하면 ref count가 1 감소했다.
C++/CX에서의 ^객체는 내부적으로는 IInspectable인터페이스를 가지는 COM객체이다. 내부적으로 AddRef()와 Release()메소드를 가지고 있지만 바깥으로 노출하진 않는다.
개념적으로는 스마트 포인터를 가지고 있지만 이 역시 C++코드로는 확인할 수 없고 컴파일 후에 어셈블리 코드를 보면 확실히 작동하는 것을 알 수 있다.
C++/CX에서 지원하는 컬렉션을 사용할때는 당연히 레퍼런스 카운트가 자동(컴파일타임)으로 관리된다. 컬렉션에 집어넣으면 카운트가 증가하고 빼면 카운트가 감소한다.
그런데 STL에 그런 배려가 있던가? 일단 표준적인 STL에서 C++/CX니 COM이니 따로 고려해줄 이유가 없다.
어떻게 STL에서 ^객체의 레퍼런스 카운트를 조정하는지 궁금했다.
다시 간단한 샘플코드를 작성해서 테스트를 시작했다.
stl::vector에 ^객체를 넣고 ref count 변화를 본다. 그리고 코드를 따라가본다.
여기서 ^객체의 ref count를 보는 방법에 대해 참고.
^객체의 레퍼런스 카운트를 직접 얻을 수는 없지만 약간의 편법을 사용해서 레퍼런스 카운트를 얻을 수 있다.
^객체는 IUnknown으로 타입캐스팅을 하면 레퍼런스 카운트를 수동으로 관리할 수 있다. 물론 권장되는 방법은 아니지만 디버그용으로 사용하면 좋다.
일단 테스트 소스코드는 다음과 같다
cppcx_stl_ref_count_src
복잡한 코드들을 한라인씩 따라가는건 무척 답답한 일이므로 ref count를 조정하는 코드를 바로 찾기로 했다.
Visual Studio의 막강한 데이터 브레이크 기능을 이용하면 메모리의 변화를 쉽게 찾을 수 있다.
우선 ^객체에서 ref count가 저장되는 메모리 위치를 찾았다. x86기준으로 ref count는 ^객체의 베이스포인터-16바이트 주소의 4바이트에 저장된다. Windows 8/8.1에선 베이스 포인터+12바이트 주소였는데 이 부분이 Windows 10 UWP에서 바뀌었다.
VS 디버거에서 이 어드레스의 4바이트 값이 변경되는 시점에 브레이크를 걸도록 데이터 브레이크를 설정한다.
금방 코드가 나왔다.
cppcx_typecast
스샷과 같이 ^객체의 베이스가 되는 Platform::Object^ 객체를 __abi_IUnknown*타입으로 캐스팅해서 __abi_AddRef()를 호출하고 있다.
콜스택을 좀더 거슬러 올라가보면 다음과 같은 코드가 나온다.
cppcx_stl_neww
C++코드로는 타입에 따라서 뭘 따로 하지는 않는다. 원소를 받아서 new로 복사본을 만들고 그걸 삽입하는걸로 보인다.(코드를 완전히 이해하지는 못했다.)
여기서 new로 메모리를 할당하는데 입력받은 타입의 오버로딩된 new를 호출한다.
Platform::Object^의 new로 진입하면 새로 메모리를 할당하고 그 주소를 리턴하는 것이 아니라 자기자신의 ref count를 증가시키고 __abi_IUnknown 포인터터로 캐스팅해서 리턴하고 있다.
Platform::Object^타입이 아닌 경우는 그냥 new 해당 타입의 메모리를 할당하고 리턴한다.
<결론>
1. C++/CX를 STL과 함께 사용할 경우 ^객체의 특성은 그대로 적용된다. ref count관리에 신경쓸 필요 없다.
2. STL코드 자체는 ^객체를 따로 구분해서 처리하지는 않는다.
3. ^객체의 ref count관리는 new를 오버로딩해서 처리한다. ref count관리가 아닌 타입이라면 new는 그냥 새로운 메모리를 리턴한다. ^객체의 경우 ref count를 증가시키고 IUnkown포인터를 리턴한다.

답글 남기기

댓글을 게시하려면 다음의 방법 중 하나를 사용하여 로그인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중