我是C ++业余爱好者。我正在编写一些Win32 API代码,并且有句柄和奇怪的复合分配对象。所以我想知道 - 是否有一些包装类可以使资源管理更容易?
例如,当我想加载一些数据时,我打开一个CreateFile()
的文件并获得HANDLE
。当我完成它之后,我应该打电话给CloseHandle()
。但是对于任何相当复杂的加载函数,将会有数十个可能的退出点,更不用说例外了。
因此,如果我可以将句柄包装在某种类型的包装类中,这将会很好,一旦执行离开作用域,它将自动调用CloseHandle()
。更好的是 - 它可以进行一些引用计数,因此我可以将其传入和传出其他函数,并且只有在最后一个引用离开范围时它才会释放资源。
这个概念很简单 - 但标准库中有类似的东西吗?顺便说一下,我正在使用Visual Studio 2008,而且我不想附加像Boost这样的第三方框架。
答案 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>;