【问题标题】:Where's the proper (resource handling) Rule of Zero? [closed]正确的(资源处理)零规则在哪里? [关闭]
【发布时间】:2013-02-14 00:39:03
【问题描述】:

这里有一篇文章讨论了一个名为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 管理基于句柄的资源,这种方式),它对我来说看起来像是一个 hack,而不是它试图解决的最佳解决方案。隐含的假设太多:

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

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

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

编辑 1

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

编辑 2

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

编辑 3

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

免责声明:

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

编辑 4

我找到了我正在寻找的东西,我已将其作为重新表述的问题的答案:https://stackoverflow.com/a/14902921。

【问题讨论】:

  • 您未能说出成语不可用的情况。是的,这个“规则”是“已经完成”。它被称为现代 C++ 风格
  • 事实上,winapi中的HANDLE是void *。这永远不会改变,并在Windows Data Types 文章中明确说明。我认为使用智能指针轻松使用 RAII 绝对没有问题。
  • 这似乎比问题更咆哮。
  • 您似乎混淆了零规则的概念(尝试重用资源管理类,这样您就不必编写新的类)与一些通用包装器的存在。列出的代码只是使用零规则的一个示例,它并没有声称能够处理任何可能的句柄类型。是否存在完全通用的句柄包装器?可能不会。
  • 让我以文章作者的身份加入。我担心我没有让自己足够清楚。正如 GManNickG 所说,零规则的想法并不是说您将需要的所有所有权原语都已经编写好了。这个想法是所有权原语是唯一应该处理所有权的事情。如果没有适合您目标的原语,请务必编写一个。只是不要将所有权与其他职责混为一谈。这不是成语:它只是适用于特定关注点的单一职责原则。

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


【解决方案1】:

背景:零规则

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

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

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

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


子弹:

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

答:在 90% 的情况下,您可以(并且应该)将 std::unique_ptr&lt;&gt; 与自定义删除器一起使用。清理的知识和责任仍然在分配者身上。它应该的方式。

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

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

他们可以。你会看例如提升::可选。或者写那个包装器。关键是,您希望资源隔离。您不想让恰好拥有/包含此类资源的类复杂化。

【讨论】:

  • boost::optional 对这些事情有何帮助? boost::optional 无法使用整数。
  • @NicolBolas 我不明白你的意思。关键是如果你想管理一个值(非指针)的生命周期,boost::optional 就在那里。在所有其他方面,值对象都是一个有争议的问题,因为没有值的所有权问题。
  • boost::optional 无法管理句柄的生命周期。句柄确实存在所有权问题。你知道,有些 API 不是 C++ API。
  • 我明白了这一切的意义,但遗憾的是,从一开始,我认为很明显这些都不能回答实际问题,在 EDIT 3 我展示了一个微不足道的情况,前一个问题是针对已经存在的工具(如果有的话)可以正确完成这项工作,因此我不需要自己私下编写。自己写回复完全忽略了实际问题。
  • @chico 现在看看 EDIT 3
【解决方案2】:

这个以句柄为中心的 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 包装器,仅提供对它的访问和移动支持。因此,绝大多数代码不需要显式的复制/移动构造函数;只是那几个叶子类。

【讨论】:

  • +1 表示最后一段。我刚刚自己添加了一些背景:)
  • 谢谢,问题是,这个通过unique_ptr 的习语不适用于我想要的某些情况,我已经使用了你所说的 RAII-wrapper。由于我不希望世界各地的所有程序员一次又一次地做他们自己的最小包装器,考虑到适当的 handle RAII 包装器的潜在可重用性,我问它可能在哪里,如果有人实现它。使用和认可。
  • +1 用于非侵入式知识示例。
  • 请注意,您可以轻松将任何类型设置为 NullablePointer - 唯一的要求是它是默认可构造的,您可以将 nullptr 分配给它并且它是显式可转换的到bool。编写一个满足这些要求的简单包装器。 (boost::optional 几乎可以,但不能从nullptr 构造)。
  • 我从未说过要修改boost::optional。把它包起来。此外,如果您可以从 nullptr 构造,则可以通过隐式转换轻松进行比较 - 您只需要与您自己进行比较即可。无论如何,它是一个非常简单的包装器,它可以使任何东西适应NullablePointer 的语义。
猜你喜欢
  • 1970-01-01
  • 2011-08-12
  • 1970-01-01
  • 1970-01-01
  • 2013-03-13
  • 2018-08-19
  • 2010-11-10
  • 2021-12-29
  • 1970-01-01
相关资源
最近更新 更多