shared_ptr 魔法 :)

新手上路,请多包涵

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

Lidström 先生声称构造 shared_ptr<Base> p(new Derived); 不需要 Base 具有虚拟析构函数:

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

Daniel Lidström :“ shared_ptr 使用它自己的析构函数来删除 Concrete 实例。这在 C++ 社区中被称为 RAII。我的建议是你尽可能了解 RAII。当你使用它时,它会让你的 C++ 编码变得更加容易所有情况下的 RAII。”

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

Daniel Lidström :“ shared_ptr 知道静态类型是 Concrete。它知道这一点,因为我在它的构造函数中传递了它!看起来有点像魔术,但我可以向你保证,它是设计使然,非常好。”

所以,评判我们。如何在不需要多态类具有虚拟析构函数的情况下实现 shared_ptr (如果是的话)?提前致谢

原文由 Armen Tsirunyan 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 418
2 个回答

是的,可以这样实现 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 对象。因此,构造函数有机会创建适当的删除器对象和引用计数器,并将指向该控制块的指针存储为数据成员。如果引用计数器达到零,则先前创建的和 Derived 删除器将用于处理对象。

C++11 标准对这个构造函数 (20.7.2.2.1) 有以下说法:

要求: p 必须可转换为 T*Y 应该是一个完整的类型。 表达式 delete p 应具有良好的格式,应具有明确的行为并且不应引发异常。

效果: 构造一个 shared_ptr 拥有 指针 p 的对象。

对于析构函数(20.7.2.2.2):

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

(强调使用粗体是我的)。

原文由 sellibitze 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

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

 void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed.

shared_ptr<Base> p(new Derived, DeleteDerived);

p 将调用 DeleteDerived 来销毁指向的对象。该实现会自动执行此操作。

原文由 Yakov Galka 发布,翻译遵循 CC BY-SA 2.5 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题