使用 std::unique_ptr 表示 Windows HANDLEs
Using std::unique_ptr for Windows HANDLEs
我正在尝试使用 std::unique_ptrs 以异常安全的方式管理 Windows HANDLE。
首先我尝试了:
struct HandleDeleter
{
void operator()( HANDLE handle )
{
if( handle )
{
FindVolumeClose( handle )
}
}
}
typedef std::unique_ptr< HANDLE, HandleDeleter > unique_vol_handle_t;
稍后在我的代码中,当我尝试使用它时:
unique_vol_handle_t volH( FindFirstVolumeW( buffer, MAX_GUID_PATH ) );
我从Visual Studio 2012RC收到以下错误:
1> error C2664: 'std::unique_ptr<_Ty,_Dx>::unique_ptr(std::nullptr_t) throw()' : cannot convert parameter 1 from 'HANDLE' to 'std::nullptr_t'
1> with
1> [
1> _Ty=HANDLE,
1> _Dx=VolumeHandleDeleter
1> ]
1> nullptr can only be converted to pointer or handle types
参考紧靠上方的volH声明行。
搜索了一段时间后,我找到了一篇博客文章,基本上是说,添加:
typedef HANDLE pointer;
到结构声明的顶部,一切都会好起来的。
我不相信,但我尝试了一下,它确实解决了错误。 我很困惑定义类型(甚至没有引用它(如何产生如此大的差异。
两个问题:
1(你能解释一下原来的错误吗? 我不明白为什么编译器指的是std::nullptr_t/nullptr
.
2(typedef如何解决这个问题(或至少看起来(? 有没有不那么"远距离的幽灵行动"解决方案?
unique_ptr
的实现会检查删除器上是否存在::pointer
类型。如果删除程序具有::pointer
类型,则此类型将用作unique_ptr
上的pointer
typedef。否则,将使用指向第一个模板参数的指针。
根据 cppreference.com,unique_ptr::pointer
类型定义为
std::remove_reference<D>::type::pointer
该类型是否存在,否则T*
我一直在为 Windows 中的各种类型的句柄执行以下操作。假设我们在某处声明了:
std::unique_ptr<void, decltype (&FindVolumeClose)> fv (nullptr, FindVolumeClose);
其中填充有:
HANDLE temp = FindFirstVolume (...);
if (temp != INVALID_HANDLE_VALUE)
fv.reset (temp);
无需声明单独的结构来包装删除程序。由于HANDLE
确实是一种void *
因此unique_ptr
以void
为类型;对于使用 DECLARE_HANDLE
宏的其他类型的句柄,可以避免这种情况:
// Manages the life of a HHOOK
std::unique_ptr<HHOOK__, decltype (&UnhookWindowsHookEx)> hook (nullptr, UnhookWindowsHookEx);
等等。
来自 MSDN 手册 unique_ptr:
存储的指向拥有资源的指针,
stored_ptr
具有类型指针。如果定义,则Del::pointer
,如果未定义,则Type *
。如果删除程序是无状态的,则存储的删除程序对象stored_deleter
不占用对象中的空间。请注意,Del 可以是引用类型。
这意味着,如果您提供删除器函子,它必须提供用于unique_ptr
的实际指针类型的pointer
类型。否则,它将是指向您提供的类型的指针,在您的情况下HANDLE*
不正确。
将std::unique_ptr
用于 Windows HANDLE 的正确(且安全(方法是这样的:
#include <windows.h>
#include <memory>
class WinHandle {
HANDLE value_;
public:
WinHandle(std::nullptr_t = nullptr) : value_(nullptr) {}
WinHandle(HANDLE value) : value_(value == INVALID_HANDLE_VALUE ? nullptr : value) {}
explicit operator bool() const { return value_ != nullptr; }
operator HANDLE() const { return value_; }
friend bool operator ==(WinHandle l, WinHandle r) { return l.value_ == r.value_; }
friend bool operator !=(WinHandle l, WinHandle r) { return !(l == r); }
struct Deleter {
typedef WinHandle pointer;
void operator()(WinHandle handle) const { CloseHandle(handle); }
};
};
inline bool operator ==(HANDLE l, WinHandle r) { return WinHandle(l) == r; }
inline bool operator !=(HANDLE l, WinHandle r) { return !(l == r); }
inline bool operator ==(WinHandle l, HANDLE r) { return l == WinHandle(r); }
inline bool operator !=(WinHandle l, HANDLE r) { return !(l == r); }
typedef std::unique_ptr<WinHandle, WinHandle::Deleter> HandlePtr;
这样INVALID_HANDLE_VALUE
被隐式视为 null,因此永远不会传递给CloseHandle
函数,并且您永远不需要显式测试它。请改用 std::unique_ptr
的operator bool()
,就像通常一样:
HandlePtr file(CreateFile(...));
if (!file) {
// handle error
}
编辑:我最初忘记了INVALID_HANDLE_VALUE
不是HANDLE
类型的唯一无效值,实际上它被许多(如果不是大多数(内核函数视为有效的伪句柄。嗯,很高兴知道。
我建议您查看向标准库添加其他 RAII 包装器的建议以及使用带有句柄的智能指针的缺点。
就个人而言,我正在使用该提案的参考实现,而不是在这些情况下使用std::unique_ptr
。
请查看窗口实现库 (WIL(,特别是用于处理无效句柄值的 unique_handle
vs unique_hfile
。
虽然@Levi Haskell,但他的回答考虑到了INVALID_HANDLE_VALUE,但它没有考虑到0是一个有效的句柄值,而是将INVALID_HANDLE_VALUE和0(或nullptr(视为相同。这是不正确的:
我的建议(基于Levi Haskell他的代码,所以归功于他(
template<void *taInvalidHandleValue>
class cWinHandle
{
HANDLE value_ { taInvalidHandleValue };
public:
cWinHandle() = default;
cWinHandle(HANDLE value) : value_(value) {}
~cWinHandle()
{
reset();
}
explicit operator bool() const { return value_ != taInvalidHandleValue; }
operator HANDLE() const { return value_; }
HANDLE get() const { return value_; }
HANDLE release() { HANDLE const result{ value_ }; value_ = taInvalidHandleValue; return result; }
friend bool operator ==(cWinHandle l, cWinHandle r) { return l.value_ == r.value_; }
friend bool operator !=(cWinHandle l, cWinHandle r) { return !(l == r); }
void reset ()
{
if (value_ != taInvalidHandleValue)
{
CloseHandle(value_);
value_ = taInvalidHandleValue;
}
}
};
inline bool operator ==(HANDLE l, cWinHandle<nullptr> r) { return cWinHandle<nullptr>(l) == r; }
inline bool operator !=(HANDLE l, cWinHandle<nullptr> r) { return !(l == r); }
inline bool operator ==(cWinHandle<nullptr> l, HANDLE r) { return l == cWinHandle<nullptr>(r); }
inline bool operator !=(cWinHandle<nullptr> l, HANDLE r) { return !(l == r); }
inline bool operator ==(HANDLE l, cWinHandle<INVALID_HANDLE_VALUE> r) { return cWinHandle<INVALID_HANDLE_VALUE>(l) == r; }
inline bool operator !=(HANDLE l, cWinHandle<INVALID_HANDLE_VALUE> r) { return !(l == r); }
inline bool operator ==(cWinHandle<INVALID_HANDLE_VALUE> l, HANDLE r) { return l == cWinHandle<INVALID_HANDLE_VALUE>(r); }
inline bool operator !=(cWinHandle<INVALID_HANDLE_VALUE> l, HANDLE r) { return !(l == r); }
- 表示"accepting anything for this template argument" C++概念的通配符
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 在Linux for Windows上编译C++代码时出错
- 在Windows上用C++裁剪HBITMAP
- C++ Windows 驱动程序MSB3030无法复制该文件,因为它找不到
- 如何加载(或映射)文件部分的最大大小,但适合在Windows上的RAM
- std::threads可以从Windows DLL中的全局变量创建/销毁吗?
- 如何将ampl中的集合表示为c++中的向量
- std::is_base_of表示ctor编译错误
- 当我编译webrtc服务器时,Windows上只支持clang-cl
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 输入中的字符串数未知(以字母表示)
- Windows/Cygwin - 不能使用 pybind11 - 犯错误
- Windows 10-使用gtkmm-3.0库和g++[包括再现]的分段故障
- 我可以信任表示整数的浮点或双精度来保持精度吗
- 在Windows中以.exe的形式运行c++
- 无法在 Windows 7、32 位上使用 tdm-gcc 构建 cpp 文件(表示 exe 不兼容)
- 使用 std::unique_ptr 表示 Windows HANDLEs
- 如何在跨平台(Windows、iOS、Android)C++应用程序中表示字符串
- Windows 中的 Control+Z 表示输入 c++ 结束的信号