如何在Windows HANDLEs中使用C++标准智能指针
How to use C++ standard smart pointers with Windows HANDLEs?
我想知道是否有一种方法可以将unique_ptr<T>
与Windows HANDLEs一起使用?
我想用调用CloseHandle
的特定handle_trats
替换std::default_delete
。问题是HANDLE
被定义为void*
unique_ptr<void>
不会编译,因为sizeof(void)
没有被定义。
到目前为止,我只看到两种可能性:
- 为HANDLEs创建一个包装类,并像这样使用它:
unique_ptr<new CHandle(h)>
。这几乎使unique_ptr<T>
本身变得毫无用处 - 使用类似于
unique_ptr<T>
的特定于HANDLE
的智能指针类
你认为什么是更好的选择?你有什么建议?
这个问题可以扩展到COM IUnknown
指针——CComPtr
可以被任何标准智能指针取代吗?
这个问题可以扩展到COM IUnknown指针-can CComPtr被任何标准智能指针所取代?
是的。您不专门化std::default_deleter
,只需替换deleter类型即可。
struct COMDeleter {
template<typename T> void operator()(T* ptr) {
ptr->Release();
}
};
unique_ptr<IUnknown, COMDeleter> ptr; // Works fine
同样的原理适用于shared_ptr
,实际上也适用于HANDLE
。
创建一个特定的智能指针类,不会花很长时间。不要滥用图书馆的课程。句柄语义与C++指针的句柄语义大不相同;首先,取消对HANDLE的引用毫无意义。
使用自定义智能句柄类的另一个原因是NULL并不总是意味着空句柄。有时它是INVALID_HANDLE_VALUE,这是不一样的(实际上是-1)。
受Alexander Drichel解决方案的启发,这里有更短的
std::unique_ptr< HANDLE, decltype(&CloseHandle) > uniqueHandle( nullptr, CloseHandle );
在MSVC 2010工作。请注意,您需要指定"&"用于decltype()中的函数名,以推导指向函数类型的指针。
您可以使用自定义的deleter 来typedef您的unique_ptr
struct handle_deleter
{
void operator()(void* handle)
{
if(handle != nullptr)
CloseHandle(handle);
}
};
typedef std::unique_ptr<void, handle_deleter> UniqueHandle_t;
UniqueHandle_t ptr(CreateFile(...));
另一种解决方案
std::unique_ptr< void, void(*)( HANDLE ) > uniqueHandle( file, []( HANDLE h ) { ::CloseHandle( h ); } );
您可以创建一个Deleter类来释放句柄,而不是调用delete()。
您可以在LINK中看到他们如何解决使用shared_ptr删除数组的问题(unique_ptr还有一个接收Delete类的构造函数)
struct handle_deleter
{
void operator ()( HANDLE handle)
{ CloseHandle(p); }
};
HANDLE blah = GetSomeHandle();
unique_ptr myPointer(blah,handle_deleter);
我不建议使用带句柄的智能指针。
我建议您查看向标准库添加额外RAII包装的建议,以及使用带句柄的智能指针的缺点。
就我个人而言,我正在使用该建议的参考实现,而不是在这些情况下使用std::unique_ptr
。
HANDLE并不总是用CloseHandle()关闭,请注意。例如,用FindNextFile()打开的HANDLE必须由FindClose()关闭。
必须使用CloseHandle()
来"关闭"句柄,而不是使用delete
。一旦它们没有在其他地方打开,Windows就会将其删除。因此,您可以编写一个调用CloseHandle()的包装器,而不是删除它。您还可以编写自己的智能指针类,这可能会更好,因为不再需要包装器了。
这里有一个我用来处理HANDLE
的方便的小"custom_ptr"。需要C++17
才能进行编译。这个想法是从另一篇SO帖子中提出的,我只添加了std::remove_pointer_t<T>
,这样我就不必将void
作为custom_ptr
类型参数传递。
定义
#include <type_traits>
#include <memory>
template<auto fn>
using deleter_from_fn = std::integral_constant<decltype( fn ), fn>;
template<typename T, auto fn>
using custom_ptr = std::unique_ptr<typename std::remove_pointer_t<T>, deleter_from_fn<fn>>;
使用
custom_ptr<HANDLE, &CloseHandle> phandle{
CreateFileMapping( INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, 1024, L"FileName" ) };
- 使用CMake检测支持的C++标准
- 如何理解C++标准N3337中的expr.const.cast子句8
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- 编译标准库类型
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 铸造标准::有没有回到原来的类型
- 标准 N3337 5.2.10 第 7 条中的C++"类型"是什么意思?
- this_thread::sleep_for和计时时钟之间的关系是否由C++11标准指定
- 标准库类型的赋值运算符的引用限定符
- 标准是否严格定义了该程序应该如何编译?
- 如何从Windows应用程序输出到标准?
- 安全到标准:移动会员?
- 如何正确将字符串转换为标准::时间::system_clock::time_point?
- 这是否符合C++标准:双响双响,例如!!(-0.0).
- 标准::变体的赋值运算符
- 捕获标准输出以压缩并使用 CTRL-C 中断会给出损坏的 zip 文件
- 如何在 Mac 上使用 c++17 并行标准库算法?
- 强枚举类型定义:Clang Bug 还是 C++11 标准不确定性?
- 并行标准::复制复杂性