2009-10-13 19 views
9

Gần đây tôi đã đăng câu hỏi chung về RAII tại SO. Tuy nhiên, tôi vẫn có một số vấn đề triển khai với ví dụ HANDLE của tôi.Thực hiện việc tuân thủ RAII HANDLE bằng shared_ptr với một deleter tùy chỉnh

A HANDLE được nhập vào void * trong windows.h. Do đó, đúng shared_ptr định nghĩa cần phải được

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

Ví dụ 1CreateToolhelp32Snapshot: trả HANDLE và các công trình.

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

Như tôi đã sử dụng void trong định nghĩa (cách chính xác là bao nhiêu?) Vấn đề tiếp tục, khi tôi cố gắng gọi cho một số chi tiết WINAPI lệnh với con trỏ này. Họ hoạt động chức năng, nhưng xấu xí và tôi chắc chắn rằng phải có một giải pháp tốt hơn.

Trong các ví dụ sau, h là một con trỏ được tạo thông qua định nghĩa ở trên cùng.

Ví dụ 2OpenProcessToken: đối số cuối cùng là PHANDLE. vừa xấu xí với dàn diễn viên.

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

Ví dụ 3Process32First: số đầu tiên là một HANDLE. Rất xấu.

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

Ví dụ 4 so sánh đơn giản với một hằng số HANDLE. Rất xấu.

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

Cách chính xác để tạo shared_ptr đúng cho HANDLE là gì?

Trả lời

9

Ví dụ 1 là OK

Ví dụ 2 là sai. Bằng cách truyền một cách mù quáng tới PHANDLE, logic shared_ptr bị bỏ qua. Nó phải là một cái gì đó như thế này thay vì:

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

hay, để gán cho một pre-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 

hoặc tạo của riêng bạn, an toàn, phiên bản của OpenProcessToken mà trả về một xử lý chia sẻ thay vì chụp một PHANDLE:

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

Ví dụ 3: Không cần phải thực hiện các đường vòng này.Điều này cần được ok:

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

Ví dụ 4: Một lần nữa, không có đường vòng:

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

Để làm cho mọi việc đẹp hơn, bạn có thể typedef cái gì đó như:

typedef shared_ptr<void> SharedHandle; 

hoặc tốt hơn nữa, nếu tất cả các chốt sẽ được đóng lại bằng CloseHandle(), tạo một lớp SharedHandle bao quanh một shared_ptr và tự động cung cấp đúng deleter:

// 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

có khả năng nào để xóa "HANDLE' không an toàn sau khi chuyển đổi nó thành an toàn trong đoạn mã ví dụ 2 đầu tiên của bạn không? – Etan

+0

Bạn có thể tạo một hàm bao bọc OpenProcessHandle() (Tôi đã thêm nó vào bài đăng) hoặc làm điều tương tự hơn trong đoạn thứ hai, với shared_h được khởi tạo thành INVALID_HANDLE_VALUE –

1

Đây là lựa chọn của tôi, mà là khá tốt đẹp ngoại trừ bạn cần phải dereference luôn sau .get() và đòi hỏi một functor hoặc lambda:

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); 
} 

thì:

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

điều tôi thích nhất về điều này là không có thêm công việc để có quyền truy cập vào điều này:

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

và tất nhiên, hàm trợ giúp hoạt động với mọi loại xử lý thích.

3

Đừng bận tâm với shared_ptr cho điều đó, hãy sử dụng ATL :: CHandle.

Đây là lý do tại sao:

  • Khi bạn nhìn thấy CHandle bạn biết rằng đó là một wrapper RAII cho một tay cầm.
  • Khi bạn thấy shared_ptr<void> bạn không biết nó là gì.
  • CHandle không chia sẻ quyền sở hữu (tuy nhiên trong một số trường hợp, bạn có thể muốn có quyền sở hữu được chia sẻ).
  • CHandle là tiêu chuẩn cho ngăn xếp phát triển cửa sổ.
  • CHandle nhỏ gọn hơn shared_ptr<void> với deleter tùy chỉnh (ít nhập/đọc).
+0

Bạn có thể mở rộng trên đó không? Các [tài liệu] (https://msdn.microsoft.com/en-us/library/5fc6ft2t.aspx) không nói nhiều, nhưng nó trông giống như một unique_ptr, tôi không thấy làm thế nào CHandle tạo điều kiện chia sẻ. – phant0m

+0

@ phant0m CHandle không cung cấp quyền sở hữu được chia sẻ. –

Các vấn đề liên quan