shared_ptr的dtor是否需要使用“删除器”? [英] Does shared_ptr's dtor require the use of a "deleter"?

查看:145
本文介绍了shared_ptr的dtor是否需要使用“删除器”?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是 广泛地 已知,您可以使用 shared_ptr 来存储指向不完全类型的指针,只要在构建 shared_ptr 期间可以删除指针(具有良好定义的行为)。例如,PIMPL技术:

It's widely known that you can use a shared_ptr to store a pointer to an incomplete type, as long as the pointer can be deleted (with well-defined behaviour) during the construction of the shared_ptr. For example, the PIMPL technique:

struct interface
{
    interface();                 // out-of-line definition required
    ~interface() = default;   // public inline member, even if implicitly defined
    void foo();
private:
    struct impl;                 // incomplete type
    std::shared_ptr<impl> pimpl; // pointer to incomplete type
};

[main.cpp]

[main.cpp]

int main()
{
    interface i;
    i.foo();
}

[interface.cpp]

[interface.cpp]

struct interface::impl
{
    void foo()
    {
        std::cout << "woof!\n";
    }
};

interface::interface()
    : pimpl( new impl ) // `delete impl` is well-formed at this point
{}

void interface::foo()
{
    pimpl->foo();
}

这是一个删除对象 pimpl(new impl)中构建 shared_ptr 期间创建所有者对象类型擦除 shared_ptr 内。这个所有者对象稍后用于销毁指向的对象。这就是为什么应该安全地提供 interface 内联析构函数。

This works as an "deleter object" "owner object" (*) is created during the construction of the shared_ptr in pimpl( new impl ), and stored after type erasure inside the shared_ptr. This "owner object" is later used to destroy the object pointed to. That's why it should be safe to provide an inline destructor of interface.

em> 问题:标准在哪里保证安全?

Question: Where does the Standard guarantee that it's safe?

的标准,见下面,但它调用自定义删除或调用delete-expression。此对象通常作为记帐对象的一部分存储,应用类型擦除并在虚拟函数中调用自定义删除器/删除表达式。在这一点上,delete-expression也应该是正确的。

github存储库中的草稿(94c8fc71,修订N3797),[util.smartptr.shared.const]

Referring to the latest draft in the github repository (94c8fc71, revising N3797), [util.smartptr.shared.const]


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

3  需要: p 应为
可转换为 T * Y 应为完整类型。表达式 delete p
应该是良好的,应该有良好定义的行为,不应该
抛出异常。

3    Requires: p shall be convertible to T*. Y shall be a complete type. The expression delete p shall be well formed, shall have well defined behavior, and shall not throw exceptions.

4  效果:构造拥有
shared_ptr 对象 p

4    Effects: Constructs a shared_ptr object that owns the pointer p.

5  后置条件: use_count()== 1&& get()== p

5    Postconditions: use_count() == 1 && get() == p.

6  抛出: bad_alloc ,或者当无法获取除内存之外的
资源时,实现定义的异常。

6    Throws: bad_alloc, or an implementation-defined exception when a resource other than memory could not be obtained.

注意:对于这个ctor, shared_ptr em>。通过 deleter ,标准似乎意味着自定义删除,例如在构建期间作为一个附加参数提供(或 shared_ptr 从另一个 shared_ptr 获取/共享一个,例如通过拷贝分配)。另见(参见[util.smartptr.shared.const] / 9)。实现(boost,libstdc ++,MSVC和我猜每个正确的实现)总是存储一个所有者对象。

Note: For this ctor, shared_ptr is not required to own a deleter. By deleter, the Standard seems to mean custom deleter, such as you provide during the construction as an additional parameter (or the shared_ptr acquires/shares one from another shared_ptr, e.g. through copy-assignment). Also see (also see [util.smartptr.shared.const]/9). The implementations (boost, libstdc++, MSVC, and I guess every sane implementation) always store an "owner object".

作为一个 delete (delete-d)定义 shared_ptr 的析构函数表达式)如果没有自定义删除者:

As a deleter is a custom deleter, the destructor of shared_ptr is defined in terms of delete (delete-expression) if there's no custom deleter:

[util.smartptr.shared.dest]

[util.smartptr.shared.dest]


~shared_ptr();

1  效果:

1    Effects:


  • 如果 *此为空白共享所有权与
    另一个 shared_ptr 实例( use_count()> 1 ),没有side $ * 一个对象 p 并且调用删除程序 d d(p)

  • 否则, *此 拥有指针 p delete p

  • If *this is empty or shares ownership with another shared_ptr instance (use_count() > 1), there are no side effects.
  • Otherwise, if *this owns an object p and a deleter d, d(p) is called.
  • Otherwise, *this owns a pointer p, and delete p is called.

我假设 intent 是一个实现需要正确删除存储的指针,即使在 shared_ptr dtor,delete-expression是不成形的或将调用UB。 (删除表达式必须格式良好,并且在ctor中有明确的行为。)所以,问题是

I'll assume the intent is that an implementation is required to correctly delete the stored pointer even if in the scope of the shared_ptr dtor, the delete-expression is ill-formed or would invoke UB. (The delete-expression must be well-formed and have well-defined behaviour in the ctor.) So, the question is

/ em>

Question: Where is this required?

(或者,我只是太吝啬,很明显,这些实现需要使用owner对象?)

(Or am I just too nit-picky and it's obvious somehow that the implementations are required to use an "owner object"?)

推荐答案


问题:

Question: Where is this required?

如果不需要析构函数具有未定义的行为,并且标准不习惯于要求未定义的行为: )

If it wasn't required the destructor would have undefined behaviour, and the standard is not in the habit of requiring undefined behaviour :-)

如果你满足构造函数的前提条件,那么析构函数不会调用未定义的行为。如何实现确保未指定,但你可以假设它得到正确,你不需要知道如何。如果实现不期望做正确的事情,那么析构函数将有一个前提条件。

If you meet the preconditions of the constructor, then the destructor will not invoke undefined behaviour. How the implementation ensures that is unspecified, but you can assume it gets it right, and you don't need to know how. If the implementation wasn't expected to Do The Right Thing then the destructor would have a precondition.


(或者我只是太nit- picky和很明显的某种方式,实现需要使用所有者对象?)

(Or am I just too nit-picky and it's obvious somehow that the implementations are required to use a "owner object"?)

是的,对象创建为拥有指针,因为引用计数(或其他簿记数据)必须在堆上,而不是任何特定 shared_ptr 实例的一部分,因为它可能需要 - 运行任何特定实例。所以是的,有一个额外的对象,它拥有的指针,你可以称为所有者对象。如果用户没有提供删除器,那么该所有者对象只会调用 delete 。例如:

Yes, there has to be some additional object created to own the pointer, because the reference counts (or other bookkeeping data) must be on the heap and not part of any specific shared_ptr instance, because it might need to out-live any specific instance. So yes, there is an extra object, which owns the pointer, which you can call an owner object. If no deleter is supplied by the user then that owner object just calls delete. For example:

template<typename T>
struct SpOwner {
  long count;
  long weak_count;
  T* ptr;
  virtual void dispose() { delete ptr; }
  // ...
};

template<typename T, typename Del>
struct SpOwnerWithDeleter : SpOwner<T> {
  Del del;
  virtual void dispose() { del(this->ptr); }
  // ...
};

现在 shared_ptr $ c> SpOwner * ,并且当计数下降到零时,调用虚函数 dispose(),它调用 delete 或调用删除器,具体取决于对象的构造方式。是否构造 SpOwner SpOwnerWithDeleter 的决定是在 shared_ptr 被构造,并且当 shared_ptr 被销毁时,该类型仍然相同,因此如果它需要处理所拥有的指针,那么它将做右Thing。

Now a shared_ptr has a SpOwner* and when the count drops to zero it invokes the virtual function dispose() which either calls delete or invokes the deleter, depending on how the object was constructed. The decision of whether to construct an SpOwner or an SpOwnerWithDeleter is made when the shared_ptr is constructed, and that type is still the same when the shared_ptr is destroyed, so if it needs to dispose of the owned pointer then it will Do The Right Thing.

这篇关于shared_ptr的dtor是否需要使用“删除器”?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆