11

这是一篇关于名为Rule of Zero的成语的文章。

这是一段摘录:

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;
};

它重用unique_ptr了 RAII 功能,因此您无需关心实现令人生畏且冗长的五规则包装器。

以这种方式呈现(以这种方式管理基于句柄的资源unique_ptr),它对我来说看起来像是一个黑客,而不是它试图解决的最佳解决方案。隐含的假设太多:

  • 一个人能够对等并使用#define(or typedef)HANDLE所基于的基本类型。对我来说,这应该是隐藏的知识,并且解决方案完全基于界面提供的内容:HANDLE.
  • 句柄,可以是任何东西,它们不需要是指针类型。

我想使用这个成语,但在我偶然发现的许多情况下它都不够用。

这个以句柄为中心的 RAII 包装器是否已经完成并可以在一些很酷的库中使用?每个人都在使用这样的工具,我不知道吗?(我认为拥有这样的工具不仅适用于一种,而且适用于许多类型的所有权)

编辑 1

这与平台特定的资源句柄无关,例如,glGenLists返回一种句柄,它是GLuint,您应该调用glDeleteLists它。如前所述,资源句柄不需要是指针类型,也不应该假设这种假设。

编辑 2

在前一个示例中,零规则通过使用已经存在的工具unique_ptr,显示为句柄管理的一个很好的快捷方式。它需要的额外假设使其不足。正确的假设是您有一个句柄,并且您有一个资源破坏函数,该函数会破坏句柄给出的资源。句柄是 a void *、 aGLuint还是什么都无关紧要,更糟糕的是,甚至不需要对等HANDLE内部类型。出于管理句柄 RAII 方式的目的,如果成语告诉它有好处,并且不能适用于这种情况,我觉得它不好,最后使用给定的工具。

编辑 3

一个说明性的情况是,假设您负责使用新的第 3 方 C 库。它包含一个FooHandle create_foo()和一个void destroy_foo(FooHandle)。所以你想,“让我们通过使用零规则来使用 FooHandle”。好的,您可以使用 a unique_ptr, aunique_ptr来回答您自己的问题吗?是FooHandle指针吗?,这样你就可以使用 a unique_ptrtoFooHandle不暴露的基本类型了吗?它是一个int?,以便您可以直接使用它,还是更好地(重新)typedef像@NicolBolas 在他的回答中所做的事情。对我来说,似乎很清楚,即使在如此微不足道的情况下,unique_ptr也已经显示出不适合管理资源句柄。

免责声明:

我试图重新表述并更好地表达自己:

编辑 4

我找到了我要找的东西,我把它作为重新制定的问题的答案:https ://stackoverflow.com/a/14902921 。

4

2 回答 2

12

背景:零规则

首先,这篇文章是关于一个更笼统的想法,而不仅仅是它提到的资源句柄包装器示例。

关键是,每当一个类包含具有“非平凡”所有权语义的成员时,该类应该负责确保包含类的正确值语义(复制、分配、移动、破坏)的机制。相反,每个组成部分本身都应该适当地实现“Rule-Of-3+”,以便复合类可以利用编译器默认的特殊成员。

此外,新的标准智能指针类型极大地简化了为包含类型实现这一点的任务。在大多数情况下,需要注意的成员是指针:std::unique_ptr、shared_ptr、weak_ptr 解决了这里的需求。

对于自定义资源类型,您可能必须编写 Rule-Of-3+ 包装类,但只需编写一次。您的其他课程将受益于零规则。


子弹:

  • 一个能够对等并使用 #define(或 typedef)HANDLE 所基于的基本类型。对我来说,这应该是隐藏的知识,并且解决方案完全基于界面提供的内容,HANDLE。

答:90% 的时间您可以(并且应该)使用std::unique_ptr<>自定义删除器。清理的知识和责任仍然在分配者身上。它应该的方式。

在其余情况下,您必须为您的特定资源编写一个包装类,否则不支持。

  • 句柄,可以是任何东西,它们不需要是指针类型。

他们能。你会看看例如 boost::optional。或者写那个包装器。关键是,您希望它为资源隔离。您不想使碰巧拥有/包含此类资源的类复杂化。

于 2013-02-14T00:58:05.967 回答
10

这个以句柄为中心的 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可以服务存储和服务任何类型的 NullablePointer 对象。HANDLE是一个 NullablePointer,所以你可以为它服务。

对于不是 NullablePointers 的对象,您必须使用其他东西。零规则的要点是使“其他东西”尽可能小。它只不过是类型的 RAII 包装器,仅提供对它的访问和移动支持。因此,绝大多数代码不需要显式的复制/移动构造函数;只是那几个叶子类。

于 2013-02-14T01:11:13.700 回答