默认情况下,“ std :: shared_ptr”是否不应该使用“ std :: default_delete”?

时间:2018-07-10 00:12:22

标签: c++ default shared-ptr delete-operator c++20

std::default_delete can be specialized以允许std::unique_ptrpainlessly manage types which have to be destroyed by calling some custom destroy-function instead of using delete p;

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

  1. 使用std::make_sharedstd::allocate_shared创建一个由共享指针管理的文件。这是首选方法,因为它将所需的两个存储块(有效负载和参考计数)合并为一个。尽管仅剩std::weak_ptr个,但是对引用计数的需求仍必定会固定有效负载的内存。

  2. 然后使用a constructor.reset()将管理分配给共享指针。

第二种情况,当不提供自定义删除器时,很有趣:

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

来自n4659(〜C ++ 17)的报价:

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

4要求:Y必须是完整类型。当delete[] p是数组类型时的表达式T,或者当delete p不是数组类型时的表达式T应当具有明确的行为,并且不抛出异常。
  5个作用:如果T不是数组类型,则构造一个拥有指针shared_ptr的{​​{1}}对象。否则,构造一个拥有p的{​​{1}}和一个未指定类型的删除器,该删除器调用shared_ptr。如果p不是数组类型,请用delete[] p启用T。如果引发异常,则在shared_from_this不是数组类型时调用p,否则将delete p调用。
  6个后置条件:T
  […]

delete[] p
     

3个效果:等效于use_count() == 1 && get() == p

我的问题是:

  1. 有没有(最好是好的)理由而不指定使用template<class Y> void reset(Y* p);
  2. 该更改是否会破坏任何有效(且可能有用的)代码?
  3. 已经有这样做的建议吗?

1 个答案:

答案 0 :(得分:4)

  

有没有(最好是好的)理由,而不是指定使用std::default_delete

因为它不会执行您想要的操作。瞧,仅仅因为您可以专注于某件事并不意味着您可以劫持它。标准说([namespace.std]):

  

仅当声明依赖于用户定义的类型且该专业化满足原始模板的标准库要求且未被明确禁止时,程序才可以将任何标准库模板的模板专业化添加到命名空间std。

std::default_delete<T>::operator()(T* ptr)的行为的标准库要求是它“在delete上调用ptr。”因此,您对它的专业化必须做同样的事情。

因此,在shared_ptr执行delete ptr;shared_ptr调用default_delete<T>{}(ptr)之间应该没有区别。

这就是unique_ptr采用删除器类型而不是依靠您对其进行专门化的原因。


从评论中:

  

专业化仅以正确的方式删除对象。

但这不是要求所要求的。它说:“在delete上呼叫ptr。”它不会说诸如“结束由ptr指向的对象的生存期”或“销毁ptr所引用的对象”之类的模棱两可的东西。它提供了必须发生的显式代码。

您的专业化必须坚持下去。

如果您不敢相信,论文P0722R1会这样说:

  

请注意,该标准要求default_delete<T>的专业化才能与调用delete p;的效果相同

很明显,作者同意专门研究default_delete并不是增加自己的行为的机制。

所以您的问题的前提是无效的。


但是,让我们假装一下您的问题是有效的,这样的专业化将起作用。是否有效,专门用于default_delete来定制删除程序行为不是预定方法。如果是这样,则根本不需要unique_ptr的删除对象。最多,您只需要一个参数即可告诉您指针的类型,默认为T*

所以这是一个很好的理由。