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

时间:2013-02-14 00:39:04

标签: c++ c++11 raii unique-ptr

这篇文章讨论了一个名为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(或typedefHANDLE所基于的基本类型。对我而言,这应该是隐藏的知识,并且解决方案完全基于界面提供的内容:HANDLE
  • 句柄,可以是任何东西,它们不需要是指针类型。

我想用这个成语,但在我偶然发现的很多情况下它都不尽如人意。

这个处理聚焦的RAII包装已经在一些很酷的库中完成并可用吗?每个人都使用这样的工具,我不知道吗? (我觉得很高兴有这样的工具不仅适用于一个,而且适用于许多类型的所有权)

编辑1

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

编辑2

Zero of Rule,在前一个示例中,通过使用现有工具unique_ptr,显示为句柄管理的一个很好的快捷方式。它所需的额外假设使其不足。正确的假设是你有一个句柄并且你有一个资源破坏函数,它会破坏句柄给出的资源。无论句柄是void *,还是GLuint,无论如何,它都无关紧要,更糟糕的是,甚至不需要对齐HANDLE内部类型。为了管理句柄RAII的方式,如果成语告诉它对它有好处,并且不能在这种情况下应用,我觉得这对它不好,最后用给定的工具。

编辑3

一个说明性的情况是,假设您负责使用新的第三方C库。它包含FooHandle create_foo()void destroy_foo(FooHandle)。所以你认为,“让我们通过采用零规则来使用FooHandle”。好的,您可以使用unique_ptrunique_ptr来自问自己? FooHandle是否为指针?,以便您使用unique_ptrFooHandle未公开基本类型?它是int吗?因此您可以直接使用它,或者更好地(重新)typedef @NicolBolas在他的回答中做了什么。对我而言,似乎很清楚,即使在如此微不足道的情况下,unique_ptr已经显示为不适合管理资源句柄的理想

声明:

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

编辑4

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

2 个答案:

答案 0 :(得分:12)

背景:零度规则

首先,这篇文章的概念要比它提到的资源句柄包装器示例更为一般。

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

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

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


子弹:

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

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

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

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

他们可以。你看看例如推动::可选。或者写那个包装器。关键是,您希望资源隔离。您不希望使碰巧拥有/包含此类资源的类复杂化。

答案 1 :(得分: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包装器,只提供访问它并移动支持。因此,绝大多数代码都不需要显式的复制/移动构造函数;只是那几个叶子类。