正确的(资源处理)零法则在哪里?

Where's the proper (resource handling) Rule of Zero?

本文关键字:在哪里 处理 资源      更新时间:2023-10-16

这是一篇关于一个名为"零法则"的成语的文章。

以下为摘录:

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_ptrFooHandle未公开的基本类型?这是一个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 包装器,只提供对它的访问和移动支持。因此,绝大多数代码不需要显式复制/移动构造函数;就那几片叶子课。