17

我是 C++ 业余爱好者。我正在编写一些 Win32 API 代码,并且有很多句柄和奇怪的复合分配对象。所以我想知道 - 是否有一些包装类可以使资源管理更容易?

例如,当我想加载一些数据时,我打开一个文件CreateFile()并获得一个HANDLE. 当我完成它时,我应该调用CloseHandle()它。但是对于任何相当复杂的加载函数,都会有几十个可能的退出点,更不用说例外了。

因此,如果我可以将句柄包装在某种包装类中,该类会CloseHandle()在执行离开范围后自动调用,那就太好了。更好的是——它可以做一些引用计数,这样我就可以将它传入和传出其他函数,并且只有在最后一个引用离开范围时才会释放资源。

这个概念很简单——但标准库中有类似的东西吗?顺便说一句,我正在使用 Visual Studio 2008,我不想附加像 Boost 之类的第 3 方框架。

4

8 回答 8

12

写你自己的。这只是几行代码。这只是一个简单的任务,不值得提供一个通用的可重用版本。

struct FileWrapper {
  FileWrapper(...) : h(CreateFile(...)) {}
  ~FileWrapper() { CloseHandle(h); }

private:
  HANDLE h;
};

想想通用版本必须做什么:它必须是可参数化的,这样你就可以指定任何一对函数,以及它们的任意数量的参数。仅仅实例化这样一个对象可能需要与上述类定义一样多的代码行。

当然,C++0x 可能会通过添加 lambda 表达式在某种程度上打破平衡。两个 lambda 表达式可以很容易地传递给通用包装类,因此一旦 C++0x 支持出现,我们可能会看到这样的通用 RAII 类添加到 Boost 或其他东西。

但目前,在需要时自行推出更容易。

至于添加引用计数,我建议不要这样做。引用计数很昂贵(突然你的句柄必须动态分配,并且每次分配都必须维护引用计数器),而且很难做到正确。这是一个在线程环境中充满微妙竞争条件的区域。

如果您确实需要引用计数,只需执行以下操作boost::shared_ptr<FileWrapper>:将您的自定义临时 RAII 类包装在shared_ptr.

于 2010-03-12T14:24:38.103 回答
3

本质上,fstream它是一个很好的文件句柄 C++ 包装器。它是标准的一部分,这意味着它是可移植的、经过良好测试的并且可以以面向对象的方式进行扩展。对于文件资源,这是一个很棒的概念。

但是,fstream仅适用于文件,不适用于通用句柄,即线程、进程、同步对象、内存映射文件等。

于 2010-03-12T14:16:44.417 回答
3

这些包装器称为 ATL。

如果您的句柄是事件或类似的,请使用CHandle类。

如果您的句柄是一个文件,请使用 CAtlFile 派生的文件,它包装了 CreateFile 和 ReadFile 等 API。

ATL 中还有其他有用的包装器,CAtlFileMapping<T>是内存映射文件的 RAII 包装器,CPath包装 shell32 API 以进行路径处理等等。

ATL 是大型库,但文件、字符串和集合等低级内容是隔离的。您可以在所有 Win32 应用程序中使用它们。只是标头,您不需要链接任何东西,或分发额外的 DLL,如 MFC 或 CRT,代码编译为 WinAPI 调用并正常工作。

它们是在VS2003或2005中从MFC中分离出来的,不记得了,即Visual Studio 2008肯定有它们。但是有一个警告,如果您使用的是 VS 的免费版本,它必须是 2015 年或更新版本。

于 2019-06-28T16:35:14.027 回答
1

这是一个基于“通过 C/C++ 的 Windows”中的 EnsureCleanup 代码:http: //www.codeproject.com/KB/cpp/template2003.aspx

于 2010-03-12T14:34:07.803 回答
0

Visual C++ 2008 通过 Feature Pack 支持 TR1,TR1 包含 shared_ptr。我会使用它——它是一个非常强大的智能指针类,可以泛化为您要求的资源管理类型。

TR1 实际上是对该标准的扩展。我相信它仍然是正式的“准标准”,但实际上你可以认为它已被锁定。

于 2010-03-12T14:21:23.270 回答
0

我认为标准库中没有任何内容,而且我也怀疑是否可以使用共享指针(如在 boost 中)(因为这些指针期望指向 HANDLE,而不是 HANDLE)。

自己写一个应该不难,遵循范围保护习语(如果你愿意,可以使用模板/函数指针等)。

于 2010-03-12T14:40:57.943 回答
0

MFC 有一些合适的原语(例如看CFile),但没有标准库。

于 2010-03-12T14:13:29.403 回答
0
template <typename Traits>
class unique_handle
{
    using pointer = typename Traits::pointer;

    pointer m_value;

    auto close() throw() -> void
    {
        if (*this)
        {
            Traits::close(m_value);
        }
    }

public:

    unique_handle(unique_handle const &) = delete;
    auto operator=(unique_handle const &)->unique_handle & = delete;

    explicit unique_handle(pointer value = Traits::invalid()) throw() :
        m_value{ value }
    {
    }

    unique_handle(unique_handle && other) throw() :
        m_value{ other.release() }
    {
    }

    auto operator=(unique_handle && other) throw() -> unique_handle &
    {
        if (this != &other)
        {
            reset(other.release());
        }

        return *this;
    }

    ~unique_handle() throw()
    {
        close();
    }

    explicit operator bool() const throw()
    {
        return m_value != Traits::invalid();
    }

    auto get() const throw() -> pointer
    {
        return m_value;
    }

    auto get_address_of() throw() -> pointer *
    {
        ASSERT(!*this);
        return &m_value;
    }

    auto release() throw() -> pointer
    {
        auto value = m_value;
        m_value = Traits::invalid();
        return value;
    }

    auto reset(pointer value = Traits::invalid()) throw() -> bool
    {
        if (m_value != value)
        {
            close();
            m_value = value;
        }

        return static_cast<bool>(*this);
    }

    auto swap(unique_handle<Traits> & other) throw() -> void
    {
        std::swap(m_value, other.m_value);
    }
};

template <typename Traits>
auto swap(unique_handle<Traits> & left,
    unique_handle<Traits> & right) throw() -> void
{
    left.swap(right);
}

template <typename Traits>
auto operator==(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() == right.get();
}

template <typename Traits>
auto operator!=(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() != right.get();
}

template <typename Traits>
auto operator<(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() < right.get();
}

template <typename Traits>
auto operator>=(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() >= right.get();
}

template <typename Traits>
auto operator>(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() > right.get();
}

template <typename Traits>
auto operator<=(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() <= right.get();
}

struct null_handle_traits
{
    using pointer = HANDLE;

    static auto invalid() throw() -> pointer
    {
        return nullptr;
    }

    static auto close(pointer value) throw() -> void
    {
        VERIFY(CloseHandle(value));
    }
};

struct invalid_handle_traits
{
    using pointer = HANDLE;

    static auto invalid() throw() -> pointer
    {
        return INVALID_HANDLE_VALUE;
    }

    static auto close(pointer value) throw() -> void
    {
        VERIFY(CloseHandle(value));
    }
};

using null_handle = unique_handle<null_handle_traits>;
using invalid_handle = unique_handle<invalid_handle_traits>;
于 2016-05-10T11:09:41.613 回答