这篇文章讨论了一个名为"零度规则"的成语.
这是一段摘录:
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; module_handle handle; };
它重用了unique_ptr
RAII功能,因此您无需关心实现令人生畏和冗长的Rule of Five包装器.
以这种方式呈现(以这种方式管理基于句柄的资源unique_ptr
),它看起来像是一个黑客,而不是它试图解决的最佳解决方案.隐含地假设了太多的假设:
一个人能够对等并使用#define
(或typedef
)HANDLE
构建的基本类型.对我来说,这应该是隐藏的知识,而解决方案完全基于界面提供的内容:HANDLE
.
句柄,可以是任何东西,它们不需要是指针类型.
我想用这个成语,但在我偶然发现的许多情况下都不尽如人意.
这个手柄是否集中了RAII包装器已经完成并可以在一些很酷的库中使用?每个人都使用这样的工具,我不知道吗?(我觉得很高兴有这样的工具不仅适用于一个,而且适用于许多类型的所有权)
这不是关于平台特定的资源句柄,例如,glGenLists
返回一种句柄,它是一个GLuint
,你应该调用glDeleteLists
它.如前所述,资源句柄不需要是指针类型,不应假设这种假设.
在前一个示例中,使用现有工具的Zero of Zero unique_ptr
显示为句柄管理的一个很好的捷径.它所需的额外假设使其不足.正确的假设是你有一个句柄,你有一个资源破坏功能,破坏句柄给出的资源.无论句柄是a void *
,a GLuint
,是什么,它都应该无关紧要,更糟糕的是,甚至不需要对HANDLE
内部类型进行对等.为了管理句柄RAII的方式,如果成语告诉它对它有好处,并且不能在这种情况下应用,我觉得这不好,最后用给定的工具.
一个说明性的情况是,假设您负责使用新的第三方C库.它包含一个FooHandle create_foo()
和一个void destroy_foo(FooHandle)
.所以你认为,"让我们通过采用零规则来使用FooHandle".好的,你去使用a unique_ptr
,a unique_ptr
来自问自己?是FooHandle
一个指针?让你使用unique_ptr
到FooHandle
的未暴露的基本类型?它是一个int
?,所以你可以直接使用它,或者更好地(重新)typedef
像@NicolBolas在他的答案中所做的那样.对于我来说,似乎很清楚,即使在这样一个平凡的情况下,unique_ptr
已经显示出作为一个不理想的管理资源句柄配合.
免责声明:
我试图重新表达并更好地表达自己:
是否有适当的"手柄所有权"?
我找到了我想要的东西,我把它作为重新提出的问题的答案: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; WndLibrary LoadWndLibrary(std::wstring const& name) { return WndLibrary(::LoadLibrary(name.c_str())); }
使用删除器,unique_ptr
可以服务存储和服务任何类型的NullablePointer对象.HANDLE
是一个NullablePointer,所以你可以为它服务.
对于不是NullablePointers的对象,您将不得不使用其他东西.零规则的要点是使"别的东西" 尽可能小.它只不过是围绕该类型的RAII包装器,只提供访问它并移动支持.因此,绝大多数代码都不需要显式的复制/移动构造函数; 只是那几个叶子类.