默认情况下,`std::shared_ptr` 不应该使用 `std::default_delete` 吗?

5

std::default_delete 可以专门化, 以使 std::unique_ptr 可以 轻松管理需要通过调用某些自定义销毁函数而不是使用delete p;来销毁的类型

C++中确保对象由std::shared_ptr管理的基本上有两种方法:

  1. 使用std::make_sharedstd::allocate_shared创建一个由共享指针管理的对象。这是首选方式,因为它将需要的内存块(有效负载和引用计数)合并为一个。但是,如果只剩下std::weak_ptr,则仍然需要引用计数来占用有效负载的内存。

  2. 之后可以使用构造函数.reset()将管理分配给共享指针。

第二种情况,在不提供自定义删除器时,是有趣的:

具体而言,它被定义为使用其自己的未指定类型的删除器,该删除器分别使用delete [] p;delete p;,具体取决于实例化std::shared_ptr是否用于数组。

摘自n4659(~C++17):

template<class Y> explicit shared_ptr(Y* p);

4 Requires: Y shall be a complete type. The expression delete[] p, when T is an array type, or delete p, when T is not an array type, shall have well-defined behavior, and shall not throw exceptions.
5 Effects: When T is not an Array type, constructs a shared_ptr object that owns the pointer p. Otherwise, constructs a shared_ptr that owns p and a deleter of an unspecified type that calls delete[] p. When T is not an array type, enables shared_from_this with p. If an exception is thrown, delete p is called when T is not an array type, delete[] p otherwise.
6 Postconditions: use_count() == 1 && get() == p.
[…]

template<class Y> void reset(Y* p);

3 Effects: Equivalent to shared_ptr(p).swap(*this).

我的问题是:

  1. 为什么没有明确指定使用 std::default_delete 是一个好的选择?
  2. 如果改变这种情况,是否会导致任何有效的(并且可能有用的)代码出现故障?
  3. 是否已经有人提出了这样的建议?

2
好的,你的专业化必须“满足原始模板的标准库要求”,而其中一部分要求是它调用 delete(或 delete[])。我不明白你如何在合法情况下将其专业化为执行其他操作。 - T.C.
当控制块与实例使用相同的内存块创建时,默认的删除操作是否仍然有效?(弱指针需要不同的销毁时间点) - JVApen
@T.C. 专业化删除对象,是唯一正确的方式。基础模板本来就是错误的,所以没有问题。 - Deduplicator
@JVApen:如果std::make_sharedstd::allocate_shared合并两个分配,就不会使用单独的删除器。这只是问题的背景。 - Deduplicator
1个回答

4
有没有一个,最好的原因是没有指定使用std :: default_delete?因为这不会做你想要的事情。看到了吗,仅仅因为您可以专门化某些内容并不意味着您可以劫持它。标准规定:程序只能在命名空间std中添加任何标准库模板的模板专业化,前提是声明依赖于用户定义的类型,并且专业化符合原始模板的标准库要求,并且未明确禁止。 std :: default_delete :: operator()(T * ptr)的标准库要求是“调用 delete on ptr 。”因此,您对其进行的专业化必须执行相同的操作。因此,让shared_ptr执行 delete ptr; 与让shared_ptr调用 default_delete {}(ptr)之间不应有任何区别。这就是为什么unique_ptr需要一个删除器类型,而不是依赖于您进行专门化的原因。

根据评论:

这个特化会以唯一适当的方式删除对象。

但是要求并没有这么说。 它说“在ptr上调用delete”。 它没有像“结束ptr指向的对象的生命周期”或“销毁ptr引用的对象”之类更模糊的说法。 它给出了必须发生的明确代码。

而且您的特化必须跟进。

如果您仍然不确定,P0722R1文件中说:

请注意,标准要求default_delete<T>的特化具有与调用delete p;相同的效果,

因此,作者明确同意专门化default_delete不是添加自己行为的机制。

因此,您问题的前提无效。


然而,假设你的问题是有效的,这样的专门化会起作用。无论是否有效,将default_delete专门化以自定义删除器行为并不是预期的方法。如果确实是意图如此,那么你根本不需要为unique_ptr使用删除器对象。最多,你只需要一个参数告诉你指针类型是什么,它将默认为T *。

所以这就是不这样做的一个很好的理由。


1
关于你回答的最后一点,假设将default_delete专门用于调用自定义的destroy而不是delete是有效的,那么当你想要为相同的T使用不同的删除器时,为unique_ptr拥有一个删除器仍然会有好处。这可能不是一个非常好的用例(强类型优于T可能是个好主意),但它确实存在。 - Justin
因此,总结一下:对于这三个问题的回答都是否定的,但是任何有用的std::default_delete特化都是无效的,所以这并不重要? - Deduplicator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接