2009-10-13 2 views
9

최근 RAII에 대한 일반적인 질문이 SO에 게시되었습니다. 그러나 HANDLE 예제에는 여전히 구현 문제가 있습니다.사용자 정의 삭제자를 사용하여 shared_ptr을 사용하여 핸들 RAII 호환 가능

HANDLEvoid *windows.h으로 입력됩니다. 따라서, 올바른 shared_ptr 정의는

std::tr1::shared_ptr<void> myHandle (INVALID_HANDLE_VALUE, CloseHandle); 

예 1CreateToolhelp32Snapshot을 할 필요가 : HANDLE와 작품을 반환합니다.

const std::tr1::shared_ptr<void> h 
    (CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL), CloseHandle); 

나는 정의 (올바른 방법이 무엇인지?) 나는이 포인터를 명령 WINAPI 좀 더 전화를 할 때 문제가 계속에서 void를 사용한다. 그들은 기능적으로 작동하지만 추악하며 더 나은 솔루션이 있어야합니다.

다음 예제에서 h은 맨 위에 정의를 통해 생성 된 포인터입니다.

예 2OpenProcessToken : 마지막 인수는 PHANDLE입니다. 중형 캐스트와 함께. 3Process32First

OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
    (PHANDLE)&h); 

예 : 첫 번째 인수는 HANDLE이다. 정말 못 생겼어. 일정한 HANDLE

Process32First(*((PHANDLE)&h), &pEntry); 

예 4 단순 비교. 정말 못 생겼어.

if (*((PHANDLE)&h) == INVALID_HANDLE) { /* do something */ } 

HANDLE에 적절한 shared_ptr을 만드는 올바른 방법은 무엇입니까?

답변

9

예 1은 OK이다.

예제 2가 잘못되었다. 맹목적으로 PHANDLE로 캐스팅하면 shared_ptr 논리가 무시됩니다. 대신 같은 것을해야한다 :

HANDLE h; 
OpenProcessToken(...., &h); 
shared_ptr<void> safe_h(h, &::CloseHandle); 

또는, 미리 exising있는 shared_ptr에 할당 :

shared_ptr<void> safe_h = .... 
{ 
    HANDLE h; 
    OpenProcessToken(...., &h); 
    safe_h.reset(h, &::CloseHandle); 
}//For extra safety, limit visibility of the naked handle 

또는 공유 핸들을 대신 반환 OpenProcessToken의 자신의 안전, 버전을 만들 PHANDLE을 복용하는 경우 :

// Using SharedHandle defined at the end of this post 
SharedHandle OpenProcess(....) 
{ 
    HANDLE h = INVALID_HANDLE_VALUE; 
    ::OpenProcessToken(...., &h); 
    return SharedHandle(h); 
} 

예 3 :이 우회로는 필요하지 않습니다.이 확인 없어야합니다 :

Process32First(h.get(), ...); 

예 4 : 다시, 더 우회 :

if (h.get() == INVALID_HANDLE){...} 

는 상황이 더 좋은 만들려면 당신이 같은 형식 정의를 수 있습니다 : 더 나은 아직

typedef shared_ptr<void> SharedHandle; 

이상을 경우 모든 핸들을 CloseHandle()로 닫고 shared_ptr을 래핑하고 자동으로 올바른 삭제자를 제공하는 SharedHandle 클래스를 만듭니다.

// Warning: Not tested. For illustration purposes only 
class SharedHandle 
{ 
public: 
    explicit SharedHandle(HANDLE h) : m_Handle(h, &::CloseHandle){}; 
    HANDLE get()const{return m_Handle.get();} 

    //Expose other shared_ptr-like methods as needed 
    //... 

private: 
    shared_ptr<void> m_Handle; 
}; 
+0

첫 번째 예제 2 코드 스 니펫에서 안전한 'HANDLE'로 변환 한 후에 안전하지 않은 HANDLE을 삭제할 수 있습니까? – Etan

+0

OpenProcessHandle()을 래핑하는 함수 (포스트에 추가)를 만들거나 shared_h가 INVALID_HANDLE_VALUE로 초기화 된 두 번째 코드 단편과 동일한 작업을 수행 할 수 있습니다. –

1

당신은 항상 .get() 후 역 참조 할 필요 제외하고는 매우 좋은이며, 펑 또는 람다 필요 내 대안입니다 살펴 보자 다음

template<typename HandleType, typename Deleter> 
std::shared_ptr<HandleType> make_shared_handle(HandleType _handle, Deleter _dx) 
{ 
    return std::shared_ptr<HandleType>(new HandleType(_handle), _dx); 
} 

:

auto closeHandleDeleter = [](HANDLE* h) { ::CloseHandle(*h); }; 
std::shared_ptr<HANDLE> sp = make_shared_handle(a_HANDLE, closeHandleDeleter); 
f_that_takes_handle(*sp.get()); 

내가 가장 좋아하는 부분은 다음과 같은 추가 작업이 없습니다.

std::weak_ptr<HANDLE> wp = sp; // Yes. This could make sense in some designs. 

물론 헬퍼 함수는 모든 핸들 유형과 함께 작동합니다.

3

그것에 대해 shared_ptr을 사용하지 마십시오. ATL :: CHandle을 사용하십시오.

  • 당신이 CHandle을 볼

    당신이 핸들에 대한 RAII 래퍼 것을 알고 왜 여기

    이다.

  • shared_ptr<void>을 볼 때 그 내용을 알 수 없습니다.
  • CHandle은 소유권을 공유하지 않습니다 (일부 경우 공유 소유권을 원할 수도 있음).
  • CHandle은 Windows 개발 스택의 표준입니다.
  • CHandle은 사용자 정의 삭제 자 (입력/읽기가 적음)가있는 shared_ptr<void>보다 컴팩트합니다.
+0

확장 할 수 있습니까? [documentation] (https://msdn.microsoft.com/en-us/library/5fc6ft2t.aspx)은별로 말하지 않지만 unique_ptr과 비슷하게 보입니다. CHandle이 공유를 어떻게하는지 보지 못합니다. – phant0m

+0

@ phant0m CHandle은 공유 소유권을 제공하지 않습니다. –

관련 문제