这是一篇关于一个名为"零法则"的成语的文章。
以下为摘录:
class module {
public:
explicit module(std::wstring const& name)
: handle { ::LoadLibrary(name.c_str()), &::FreeLibrary } {}
// other module related functions go here
private:
using module_handle = std::unique_ptr<void, decltype(&::FreeLibrary)>;
module_handle handle;
};
它重用了 RAII unique_ptr
功能,因此您无需关心实现令人生畏且冗长的五规则包装器。
以这种方式呈现(使用 unique_ptr
管理基于句柄的资源,这样(,它对我来说似乎是一个黑客,而不是它试图解决的最佳解决方案。太多的假设被隐含地假设:
- 一个人能够对等并使用
#define
(或typedef
(的基本类型HANDLE
所基于的。对我来说,这应该是隐藏的知识,并且解决方案完全基于界面提供的内容:HANDLE
. - 句柄可以是任何东西,它们不需要是指针类型。
我想使用这个成语,但在我偶然发现的许多情况下,它都不足。
这个以句柄为中心的 RAII 包装器是否已经完成并且可以在一些很酷的库中使用?每个人都在使用这样的工具而我不知道吗?(我认为不仅为一个人,而且为多种类型的所有权提供这样的工具真是太好了(
编辑 1
这与特定于平台的资源句柄无关,例如,glGenLists
返回一种句柄,它是一个GLuint
,您应该在其上调用glDeleteLists
。如前所述,资源句柄不需要是指针类型,不应假定此假设。
编辑 2
在前一个示例中,零规则通过使用现有的工具 unique_ptr
,显示为句柄管理的良好快捷方式。它所需的额外假设使其不足。正确的假设是,你有一个句柄,并且你有一个资源销毁函数来销毁句柄给出的资源。无论手柄是void *
,还是GLuint
,无论如何,都无关紧要,更糟糕的是,甚至不应该要求HANDLE
内部类型进行窥视。为了管理处理RAII方式,如果成语说它对此有好处,并且不能在这种情况下适用,我觉得它对此不好,最后使用给定的工具。
编辑 3
一个说明性的情况是,假设您负责使用新的第三方 C 库。它包含一个FooHandle create_foo()
和一个void destroy_foo(FooHandle)
。所以你会想,"让我们使用FooHandle的,采用零法则"。好吧,你用一个unique_ptr
,一个unique_ptr
你问自己的问题?FooHandle
是指针吗?,以便您使用unique_ptr
来FooHandle
未公开的基本类型?这是一个int
?,以便您可以直接使用它,还是最好像@NicolBolas在他的回答中所做的那样(重新(typedef
事情。对我来说,似乎很明显,即使在如此微不足道的情况下,unique_ptr
也已经表明不适合管理资源句柄。
免責聲明:
我试图重新表述并更好地表达自己:
- 是否有适当的"包内所有权"可用?
编辑 4
我已经找到了我想要的东西,我把它作为重新制定的问题中的答案:https://stackoverflow.com/a/14902921。
背景:零法则
首先,这篇文章是一个更一般的想法,而不仅仅是它提到的资源句柄包装器示例。
关键是,每当一个类包含具有"非平凡"所有权语义的成员时,该类不应该负责确保包含类的正确值语义(复制、赋值、移动、破坏(的机制。 相反,每个组成部分都应该根据需要自行实现"Rule-Of-3+",以便复合类可以利用编译器默认的特殊成员。
此外,新的标准智能指针类型大大简化了为包含类型执行此操作的任务。在大多数情况下,需要注意的成员将是指针:std::unique_ptr,shared_ptr,weak_ptr解决这里的需求。
对于自定义资源类型,您可能必须编写 Rule-Of-3+ 包装器类,但只能编写一次。您的其他班级将受益于零规则。
子弹:
- 一个人能够对等并使用构建 #define(或typedef(HANDLE的基本类型。对我来说,这应该是隐藏的知识,并且解决方案完全基于界面提供的内容,HANDLE。
答:90% 的时间您可以(并且应该(将std::unique_ptr<>
与自定义删除器一起使用。清理的知识和责任仍由分配器承担。它应该的方式。
在其余情况下,必须为特定资源编写一个不受支持的包装类。
- 句柄可以是任何东西,它们不需要是指针类型。
他们可以。你会看看例如 boost::optional。或者写那个包装器。关键是,您希望它与资源隔离。您不想使碰巧拥有/包含此类资源的类复杂化。
这个以句柄为中心的 RAII 包装器是否已经完成并且可以在一些很酷的库中使用?
对于您的情况,它称为 unique_ptr
.观察:
struct WndLibDeleter
{
typedef HANDLE pointer;
void operator()(HANDLE h) {::FreeLibrary(h);}
};
using WndLibrary = std::unique_ptr<HANDLE, WndLibDeleter>;
WndLibrary LoadWndLibrary(std::wstring const& name)
{
return WndLibrary(::LoadLibrary(name.c_str()));
}
使用删除器,unique_ptr
可以服务存储和服务任何类型的可空指针对象。 HANDLE
是一个可为空的指针,所以你可以为它提供服务。
对于不是 NullablePointer 的对象,必须使用其他对象。零法则的要点是使"其他东西"尽可能小。它只不过是围绕该类型的 RAII 包装器,只提供对它的访问和移动支持。因此,绝大多数代码不需要显式复制/移动构造函数;就那几片叶子课。