22

我正在尝试使用 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 是如何解决这个问题的(或者至少看起来是这样的)?对此是否有一个不那么“幽灵般的远距离行动”解决方案?

4

7 回答 7

26

unique_ptr检查删除器上是否存在::pointer类型的实现。如果删除器具有类型,::pointer则此类型用作pointer. unique_ptr否则使用指向第一个模板参数的指针。

根据cppreference.comunique_ptr::pointer类型定义为

std::remove_reference<D>::type::pointer如果该类型存在,否则T*

于 2012-08-29T19:14:35.893 回答
6

MSDN 手册 unique_ptr

存储的指向拥有资源的stored_ptr指针具有类型指针。Del::pointer如果已定义,则为,如果未定义Type *如果删除器是无状态的,则存储的删除器对象stored_deleter不占用对象中的空间。请注意,Del 可以是引用类型。

这意味着如果你提供一个删除器函子,它必须提供一个pointer用于unique_ptr. 否则它将是指向您提供的类型的指针,在您的情况下HANDLE*这是不正确的。

于 2012-08-29T19:17:28.903 回答
5

我一直在为 Windows 中的各种类型的句柄执行以下操作。假设我们已经在某处声明:

std::unique_ptr<void, decltype (&FindVolumeClose)> fv (nullptr, FindVolumeClose);

这填充有:

HANDLE temp = FindFirstVolume (...);
if (temp != INVALID_HANDLE_VALUE)
    fv.reset (temp);

无需声明单独的结构来包装删除器。因为HANDLE真的是一个void *作为unique_ptrvoid的类型;对于使用DECLARE_HANDLE宏的其他类型的句柄,可以避免这种情况:

// Manages the life of a HHOOK
std::unique_ptr<HHOOK__, decltype (&UnhookWindowsHookEx)> hook (nullptr, UnhookWindowsHookEx);

等等。

于 2013-06-21T20:41:56.463 回答
5

用于 Windows HANDLE的正确(和安全)方法std::unique_ptr是这样的:

#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's operator bool()

HandlePtr file(CreateFile(...));
if (!file) {
    // handle error
}

编辑:我最初忘记了这INVALID_HANDLE_VALUE 不是该类型的唯一无效,实际上它被许多(如果不是大多数)内核函数HANDLE视为有效的伪句柄。嗯,很高兴知道。

于 2015-12-21T22:58:19.183 回答
3

我建议您查看A Proposal to Add additional RAII Wrappers to the Standard Library以及使用带句柄的智能指针的缺点

就个人而言,我正在使用该提案的参考实现,而不是在std::unique_ptr这些情况下使用。

于 2013-02-15T23:44:39.637 回答
1

请查看Window Implementation Libraries (WIL),尤其是处理无效句柄值的unique_handlevs。unique_hfile

于 2020-01-21T16:58:43.633 回答
-1

虽然@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); }
于 2017-12-19T09:45:06.670 回答