我应该使用C ++中的哪个包装类进行自动资源管理?

时间:2010-03-12 14:11:52

标签: c++ raii resource-management

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

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

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

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

8 个答案:

答案 0 :(得分:11)

自己写。它只有几行代码。这是一项非常简单的任务,它不是值得来提供通用的可重用版本。

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

private:
  HANDLE h;
};

考虑通用版本必须做什么:它必须是可参数化的,因此您可以指定任何函数对,以及任何个参数他们。只是实例化这样的对象可能需要与上面的类定义一样多的代码行。

当然,C ++ 0x可能会在添加lambda表达式时略微平衡。两个lambda表达式可以很容易地传递给一个通用的包装类,所以一旦C ++ 0x支持,我们可能看到添加到Boost中的这样一个泛型RAII类。

但目前,只要你需要就可以更轻松地推出自己的产品。

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

如果需要引用计数,只需执行boost::shared_ptr<FileWrapper>之类的操作:将自定义ad-hoc RAII类包装在shared_ptr中。

答案 1 :(得分:2)

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

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

答案 2 :(得分:2)

这些包装器称为ATL。

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

如果句柄是文件,请使用CAtlFile派生的文件,它包装诸如CreateFile和ReadFile之类的API。

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

ATL是大型库,但文件,字符串和集合之类的低级内容却是隔离的。您可以在所有Win32应用程序中使用它们。仅是标题,您无需链接任何内容,也无需分发MFC或CRT之类的其他DLL,代码可以编译为WinAPI调用,并且可以正常工作。

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

答案 3 :(得分:1)

这是一个基于'Windows via C / C ++'的EnsureCleanup代码的代码: http://www.codeproject.com/KB/cpp/template2003.aspx

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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

TR1实际上是标准的延伸。我相信它仍然是正式的“预标准”,但实际上你可以认为它被锁定了。

答案 6 :(得分:0)

我认为标准库中没有任何东西,我也怀疑可以使用共享指针(如在boost中)(因为那些指望HANDLE,而不是HANDLE)。

按照scope guard惯用法(并使用模板/函数指针等,如果你这样选择),你自己写一个并不难。

答案 7 :(得分: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>;