如何可能(如果可以的话)实现shared_ptr而不需要多态类具有虚拟析构函数?

发布于 2024-09-27 04:04:41 字数 921 浏览 4 评论 0原文

先生。 Lidström 和我发生了争论 :)

Lidström 先生的主张是构造 shared_ptr。 p(new Derived); 不需要 Base 有虚拟析构函数:

Armen Tsirunyan:“真的吗?shared_ptr会正确清理吗?在这种情况下,您能否演示一下如何实现该效果?”

Daniel Lidström:“shared_ptr 使用自己的析构函数来删除 Concrete 实例。这在 C++ 社区中称为 RAII。我的建议是您学习所有内容当您在所有情况下使用 RAII 时,您可以了解 RAII。”

Armen Tsirunyan:“我了解 RAII,而且我也知道最终 shared_ptr 析构函数可能会在 pn 达到 0 时删除存储的 px。但是如果 px 具有静态指向 Base 的类型指针和指向 Derived 的动态类型指针,那么除非 Base 有虚拟析构函数,否则这将导致未定义的行为。我错了。”

Daniel Lidström:“shared_ptr 知道静态类型是 Concrete。自从我在其构造函数中传递它以来,它就知道这一点!看起来有点像魔术,但我可以向您保证,这是经过设计的,而且非常漂亮。”

所以,评判我们吧。在不要求多态类具有虚拟析构函数的情况下,如何可能(如果可以的话)实现shared_ptr

Mr. Lidström and I had an argument :)

Mr. Lidström's claim is that a construct shared_ptr<Base> p(new Derived); doesn't require Base to have a virtual destructor:

Armen Tsirunyan: "Really? Will the shared_ptr clean up correctly? Could you please in this case demonstrate how that effect could be implemented?"

Daniel Lidström: "The shared_ptr uses its own destructor to delete the Concrete instance. This is known as RAII within the C++ community. My advice is that you learn all you can about RAII. It will make your C++ coding so much easier when you use RAII in all situations."

Armen Tsirunyan: "I know about RAII, and I also know that eventually the shared_ptr destructor may delete the stored px when pn reaches 0. But if px had static type pointer to Base and dynamic type pointer to Derived, then unless Base has a virtual destructor, this will result in undefined behavior. Correct me if I am wrong."

Daniel Lidström: "The shared_ptr knows the static type is Concrete. It knows this since I passed it in its constructor! Seems a bit like magic, but I can assure you it is by design and extremely nice."

So, judge us. How is it possible (if it is) to implement shared_ptr without requiring polymorphic classes to have virtual destructor?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

﹏半生如梦愿梦如真 2024-10-04 04:04:41

是的,可以通过这种方式实现shared_ptr。 Boost 确实如此,C++11 标准也要求这种行为。作为增加的灵活性,shared_ptr 不仅仅管理引用计数器。所谓的删除器通常被放入也包含引用计数器的同一内存块中。但有趣的是,这个删除器的类型不是shared_ptr类型的一部分。这称为“类型擦除”,基本上与用于实现“多态函数”boost::functionstd::function 用于隐藏实际函子类型的技术相同。为了使您的示例正常工作,我们需要一个模板化构造函数:

template<class T>
class shared_ptr
{
public:
   ...
   template<class Y>
   explicit shared_ptr(Y* p);
   ...
};

因此,如果您将其与您的类 BaseDerived 一起使用......

class Base {};
class Derived : public Base {};

int main() {
   shared_ptr<Base> sp (new Derived);
}

模板化构造函数与 Y=Derived 用于构造 shared_ptr 对象。因此,构造函数有机会创建适当的删除器对象和引用计数器,并将指向该控制块的指针存储为数据成员。如果引用计数器达到零,则先前创建的且派生感知的删除器将用于处置该对象。

C++11 标准对此构造函数有以下规定 (20.7.2.2.1):

要求: p 必须可转换为 T*Y 应为完整类型。 表达式delete p应格式良好,应具有明确定义的行为并且不应抛出异常。

效果:构造一个拥有指针pshared_ptr对象。

对于析构函数(20.7.2.2.2):

效果:如果*this或与另一个shared_ptr实例共享所有权(use_count () > 1),没有副作用。
否则,如果 *this 拥有对象 p 和删除器 d,则调用 d(p)
否则,如果*this拥有指针p,则调用delete p

(使用粗体字体强调的是矿)。

Yes, it is possible to implement shared_ptr that way. Boost does and the C++11 standard also requires this behaviour. As an added flexibility shared_ptr manages more than just a reference counter. A so-called deleter is usually put into the same memory block that also contains the reference counters. But the fun part is that the type of this deleter is not part of the shared_ptr type. This is called "type erasure" and is basically the same technique used for implementing the "polymorphic functions" boost::function or std::function for hiding the actual functor's type. To make your example work, we need a templated constructor:

template<class T>
class shared_ptr
{
public:
   ...
   template<class Y>
   explicit shared_ptr(Y* p);
   ...
};

So, if you use this with your classes Base and Derived ...

class Base {};
class Derived : public Base {};

int main() {
   shared_ptr<Base> sp (new Derived);
}

... the templated constructor with Y=Derived is used to construct the shared_ptr object. The constructor has thus the chance to create the appropriate deleter object and reference counters and stores a pointer to this control block as a data member. If the reference counter reaches zero, the previously created and Derived-aware deleter will be used to dispose of the object.

The C++11 standard has the following to say about this constructor (20.7.2.2.1):

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

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

And for the destructor (20.7.2.2.2):

Effects: 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, if *this owns a pointer p, and delete p is called.

(emphasis using bold font is mine).

芯好空 2024-10-04 04:04:41

当创建shared_ptr时,它会在其内部存储一个deleter对象。当shared_ptr即将释放指向的资源时调用此对象。由于您知道如何在构造时销毁资源,因此即使对于不完整的类型,您也可以使用 shared_ptr 。创建 shared_ptr 的人已经在那里存储了正确的删除器。

例如,您可以创建一个自定义删除器:

void DeleteDerived(Derived* d) { delete d; }
shared_ptr<Base> p(new Derived, DeleteDerived);

p 将调用 DeleteDerived 来销毁指向的对象。

这是 shared_ptr 构造函数自动执行的操作,因此在实践中,您不需要实现此类删除器,除非您通过调用 delete 以外的方式进行释放。

When shared_ptr is created it stores a deleter object inside itself. This object is called when the shared_ptr is about to free the pointed resource. Since you know how to destroy the resource at the point of construction you can use shared_ptr even with incomplete types. Whoever created the shared_ptr has already stored a correct deleter there.

For example, you can create a custom deleter:

void DeleteDerived(Derived* d) { delete d; }
shared_ptr<Base> p(new Derived, DeleteDerived);

p will call DeleteDerived to destroy the pointed object.

This is what the shared_ptr constructor does automatically, so in practice you don't need to implement that sort of a deleter unless you free by means other than a call to delete.

哽咽笑 2024-10-04 04:04:41

简单地说,

shared_ptr 使用特殊的删除器函数,该函数由始终使用的构造函数创建
给定对象的析构函数而不是 Base 的析构函数,这是模板元编程的一些工作,但它有效。

类似的东西

template<typename SomeType>
shared_ptr(SomeType *p)
{
   this->destroyer = destroyer_function<SomeType>(p);
   ...
}

Simply,

shared_ptr uses special deleter function that is created by constructor that always uses
the destructor of the given object and not the destructor of Base, this is a bit of work with template meta programming, but it works.

Something like that

template<typename SomeType>
shared_ptr(SomeType *p)
{
   this->destroyer = destroyer_function<SomeType>(p);
   ...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文