是否有适用于“手柄”的适当“所有权在一个包中”?
Posted
技术标签:
【中文标题】是否有适用于“手柄”的适当“所有权在一个包中”?【英文标题】:Is there a proper 'ownership-in-a-package' for 'handles' available? 【发布时间】:2013-02-14 15:22:02 【问题描述】:Handles 具有除指针之外的适当语义。所以对我来说一个这样的例子(摘自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
作为句柄的“包中所有权”是一个不好的例子。首先,它利用句柄是指针类型的内部知识,并使用它为“不透明”句柄类型构建的基本类型创建unique_ptr
。
句柄可以是任何类型,它们可能是指针,可能是索引或谁知道。最重要的是,您手头(例如大多数 C API)是一个句柄及其资源释放函数。
在句柄语义中工作是否存在适当的“包中所有权”?我的意思是,已经公开供一个人使用?
对我来说,unique_ptr
等。人。不起作用,我必须对句柄类型是做出不必要的假设,而我想要的只是通过不透明的句柄类型及其释放获得“包中的所有权”功能,单独。
查看句柄类型内部以根据此信息进行构造是没有意义的。是把手,应该没关系。
我在这里引用另一个SO用户another question'sanswer的感受:
创建一个特定的“智能指针”类,不会花很长时间。不要滥用 图书馆类。句柄语义与 C++ 指针;一方面,取消引用 HANDLE 是没有意义的。
使用自定义智能句柄类的另一个原因 - NULL 不 总是意味着一个空的句柄。有时是 INVALID_HANDLE_VALUE, 这不一样。
免责声明:
这个问题重新表述并建立在这个问题的基础上:
Where's the proper (resource handling) Rule of Zero?【问题讨论】:
我不同意你的前提。unique_ptr
可能有点用词不当——它处理资源。 HANDLE
也处理资源。这是一个完美的匹配。
我的前提不仅仅基于类型名称...
您的问题是到底是什么?你不喜欢unique_ptr
这个名字?
@EtiennedeMartel:要将unique_ptr
用于非指针句柄,您需要一个特殊的删除器来定义嵌套的pointer
类型,而pointer
类型不能简单地是,比如说int
,因为int
不符合unique_ptr
想要的NullablePointer 要求。但是,you can write a simple wrapper 使 anything 适应那些可空指针要求。如果您愿意,还可以添加一个转发到*value
的一元operator*
和一个operator->
,这样您就可以实际使用*up
和up->foo()
。
@Xeo:如果您要编写该包装器的麻烦...为什么不编写一个可以保存任何值并在其上调用给定函数/函子的适当 RAII 对象销毁时键入?这比使用unique_ptr
来处理非指针的事情要简单得多。说真的,为一个对象编写复制/移动构造函数并不是一个繁重的负担。
【参考方案1】:
unique_ptr
类型不如短语“handle”通用,是的。但为什么不应该呢?您的“处理”示例之一(例如,整数索引)与unique_ptr
一样通用。您无法将一种特定类型的句柄与“所有句柄”进行比较。
如果你想要一个单一的、具体的 C++ 类型(或类型模板),它是一个句柄,但实际上没有定义任何特定的处理语义,那么......我帮不了你。我认为没有人能轻松应对。
【讨论】:
它不太通用,因此应该在合适的地方使用。当人们开始查找HANDLE
(假定是不透明的,手柄的主要特征之一)的基础,并在不太通用的工具中使用该信息时,对我来说,这是难闻的气味。当然,对于我来说,可以断言HANDLE
是void *
,那么其他基于句柄的库呢,你会假设断言消失了吗?而且这个假设不是句柄语义的一部分,HANDLE
是不透明的。
@chico:“其他基于句柄的库”有不同的句柄类型。 unique_ptr
是 one 句柄类型。您在编码中遇到了什么实际问题?
unique_ptr
是一种句柄类型吗?我不明白你的意思。 HANDLE
是 句柄 类型。我的实际问题是忽略它是不透明的 来利用unique_ptr
来管理它所引用的资源的生命周期。我认为这是一种不好的做法,并且我要求一种已知的“所有权在一个包中”的替代方案,它是正确的。我的意思是,不是那样的违规封装。 句柄类型是不透明的,一个好的工具会处理句柄,最后是整数和基于指针的,同样的语法方式。
@chico:你一直说句柄类型必须是不透明的,但我看不出有任何理由。 unique_ptr
非常适合具有指针语义的句柄。当然,它没有非指针语义。这是一种的句柄。
句柄是否被假定为不透明并不重要,重要的是它们通常被假定为不透明。您可以在内部将您的句柄类型构建为void *
,并为您的客户预先声明它将是这样的。您的客户可以安全地使用unique_ptr<void>
(看起来unique_ptr<void>
并没有什么意义,您无法从句柄类型推断任何内容,它不是指向无类型的指针)。但这是您的特殊手柄的情况。一般而言,此过程不适用于句柄。【参考方案2】:
std::experimental::unique_resource
【讨论】:
为什么要强制客户端在使用位置手动提供清理(即传递给make_scoped_resource
的lambda)?
@R.MartinhoFernandes 不是unique_ptr
和deleter
的情况吗?如果在句柄语义中使用 default_deleter 是否有意义?我想在大多数情况下,你会得到更短的语法,并且你有 std::unique_resource
选项不会强制这样做。但是,我仍然想知道一个好的解决方案。
如果您的删除器是默认可构造的,则不会。关键是 make_scoped_resource,就像 unique_ptr 的带有删除器的 ctor 一样,永远不应该在客户端代码中使用。它应该只在库函数中使用。像make_gl_list
这样的东西,内部可能使用make_scoped_resource
,但不会强迫客户关心它。
@R.MartinhoFernandes 为什么要强制语言用户将所有涉及遗留 API 使用句柄的情况封装起来?库函数可能需要为某些操作调用一次遗留 API,为什么要在这种微不足道的情况下强制包装?这是与 C 交互的人的常见用法,为什么不提供好的资源来处理这个问题呢?小包装器,私有的类似功能的包装器库,代码技巧,这一切都必须去!
什么。在您的代码示例中,用户本身必须 处理遗留 API(请参阅那里对 glDeleteLists 的调用?)。那是应该消失的。以上是关于是否有适用于“手柄”的适当“所有权在一个包中”?的主要内容,如果未能解决你的问题,请参考以下文章