【问题标题】:What is the virtual function inside of shared_ptr's Control Block?shared_ptr 的控制块内部的虚函数是什么?
【发布时间】:2015-01-31 15:54:19
【问题描述】:

我在 Scott Meyers 的“Effective Modern C++”一书中读到了一篇关于 shared_ptr 的“项目”,他说如下:

通常的控制块实现比较多 比你想象的复杂。它利用了继承,甚至还有一个虚函数。 (用于确保正确销毁指向的对象。) 这意味着使用 std::shared_ptrs 也会产生控制块使用的虚函数的机器成本。

然后他没有解释虚函数的确切作用。据我所知,删除指向对象的正确方法是使用删除器或类型擦除。所以,请解释一下这是关于什么的。

【问题讨论】:

  • 还是他给“类型擦除”机制起了一个“虚函数”的名字?
  • Type-erasing the deleter 需要使用虚函数。 (好吧,从技术上讲,它不需要它,但这是实现它的最方便的方法。)

标签: c++ pointers shared-ptr smart-pointers


【解决方案1】:

需要虚函数来确保正确删除正在共享的对象。与unique_ptr 不同,shared_ptr 在实例化其模板时不需要完全了解类型。例如。你可以这样做:

class Foo;
std::shared_ptr<Foo> foo = make_foo();

请注意,在上面的代码中,我们没有完整的 Foo 类型,只有前向声明。如果我们让foo 超出范围,它指向的对象将被正确删除,因为当Foomake_foo 中创建时,还创建了一个知道Foo 完整类型的删除器,因此可以调用适当的析构函数。 (例如,make_foo 可能会创建一个继承自 FooBar 并返回它。shared_ptr 会很好地处理这个问题。)

shared_ptr 创建的用于管理Foo 删除的删除器对象上的函数将是虚拟的,允许shared_ptr 调用正确的析构函数。

大概是这样的:

struct deleter_interface {
    virtual void ~deleter_interface = default;
    virtual void dispose() = 0;
};

template <typename T>
struct deleter : deleter_interface {
    T* ptr_;
    deleter(T* ptr) : ptr_(ptr) {}
    virtual void dispose() { delete ptr_; }
};

template <typename T>
shared_ptr {
    T* ptr_;
    deleter_interface* deleter_;
    ...
};

template <typename T>
shared_ptr<T>::shared_ptr<T>(T* ptr)
    : ptr_(ptr), deleter_(new deleter<T>(ptr)) {}

template <typename T>
shared_ptr<T>::~shared_ptr<T>() { deleter_->dispose(); delete deleter_; }

虽然这看起来更复杂,但这是绝对必要的,它允许在没有完整类型的情况下使用shared_ptr。例如,如果您想这样做:

在 a.h 中:

struct Foo;
std::shared_ptr<Foo> a = make_object();
// ... let a go out of scope

在 a.cc 中:

struct Foo { ... };
struct Bar : Foo { ... };
std::shared_ptr<Foo> make_object() { return std::shared_ptr<Foo>(new Bar); }

如果我们没有在删除代码中使用虚函数,那么Bar 将不会被正确破坏。使用虚函数,标题 (a.h) 永远不会看到 FooBar 的定义并不重要。

【讨论】:

  • hm... 我认为没有办法创建deleter_interface 对象,因为它具有纯虚函数。以及deleter_ 如何找出类型?我们可以在没有虚函数的情况下实现这种行为。
  • 是的。我匆忙写代码太快了。我已经更新,所以它确实有效。这种行为可以在没有虚函数的情况下完成,但前提是您在实例化 shared_ptr 时始终具有完整类型。我会在答案中添加一些额外的解释。
  • 我的意思是我们可以有一个模板构造函数,它接收另一个类型的参数。例如:template &lt;class T&gt; class Shared { template &lt;class U&gt; Shared(U* ptr):deleter&lt;U&gt;(ptr) ; //now deleter knows actual type of ptr 但是您向我展示了另一种实现此功能的方法 :) 谢谢!附:如果我想通过我的删除器怎么办?
  • 你可以,但你仍然需要删除器中的虚函数,因为 shared_ptr 析构函数的实例化不会看到原始类型。
  • 为什么看不到原始类型?以免考虑以下实例化 Shared&lt;Base&gt; ptr (new Derived()); 。这里构造函数接收 Derived* 因此 U 被推断为 Deriveddeleter&lt;U&gt; 实例化为 deleter&lt;Derived&gt; 。所以,shared_ptr 的析构函数不知道原始类型,但 deleter 知道
猜你喜欢
  • 1970-01-01
  • 2019-11-30
  • 1970-01-01
  • 2011-10-14
  • 1970-01-01
  • 2017-05-05
  • 2012-01-11
  • 2021-10-18
  • 2017-11-04
相关资源
最近更新 更多