【问题标题】:Is a virtual destructor needed for your Interface, if you always store it in a shared_ptr?如果您始终将其存储在 shared_ptr 中,您的接口是否需要虚拟析构函数?
【发布时间】:2011-10-01 20:38:51
【问题描述】:

由于boost::/std::shared_ptr 具有擦除其删除器的类型的优势,因此您可以做一些不错的事情,例如

#include <memory>

typedef std::shared_ptr<void> gc_ptr;

int main(){
  gc_ptr p1 = new int(42);
  gc_ptr p2 = new float(3.14159);
  gc_ptr p3 = new char('o');
}

由于保存了正确的删除器,这将正确删除所有指针。

如果您确保接口的每个实现总是使用shared_ptr&lt;Interface&gt;(或make_shared&lt;Interface&gt;)创建,那么您真的需要virtual 析构函数吗?无论如何我都会声明它virtual,但我只是想知道,因为shared_ptr 将始终删除它初始化的类型(除非给出另一个自定义删除器)。

【问题讨论】:

  • shared_ptr magic :) 的可能重复项
  • @Armen:这不是重复的,他不是问shared_ptr 是如何做到的,而是你是否应该使用虚拟析构函数知道 shared_ptr 做到这一点魔术。
  • @David:不,他没有。他说无论如何他都会使用虚拟析构函数。他在问没有一个是否可以。所以它是重复的
  • 是的,这是真的。但是我个人会担心这样做。有一天,我将决定“哦,这不需要 shared_ptr,我将只使用指向基类的指针”,然后一切都微妙地中断了。我认为它是脆弱的代码,外部代码对如何实现类做出合理的假设很容易被破坏,除非我能证明有一个只能通过这样做才能满足的要求,否则我不会这样做。
  • @Kerrek:不,这三种情况的删除器都不同。他们都可能采用void*,但将其转换为正确的类型,分别为intfloatchar

标签: c++ boost c++11 shared-ptr virtual-destructor


【解决方案1】:

对于要派生的类,我仍然会遵循通用规则:

提供公共虚拟析构函数或受保护的非虚拟析构函数

原因是你不能控制所有的使用,这个简单的规则意味着如果你试图通过层次结构中的错误级别delete,编译器会标记。考虑shared_ptr 不保证它会调用适当的析构函数,只保证它会调用用作参数的静态类型的析构函数:

base* foo();
shared_ptr<base> p( foo() );

如果base 有一个公共的非虚拟析构函数并且foo 返回一个派生自base 的类型,那么shared_ptr 将无法调用正确的析构函数。如果base的析构函数是虚拟的,一切都会好的,如果它被保护了,编译器会告诉你那里有错误。

【讨论】:

  • “无论如何我都会宣布它是虚拟的,[...]”。 :) 关于无法控制所有实例化点的好处。虽然,您总是可以使用命名的构造函数,但这可能看起来不太好。
  • 警告:受保护的析构函数当前不会为 is_nothrow_destructible&lt;T&gt;::value 回答 true,即使它们没有引发异常。因此,我倾向于公共选项。
  • @Howard:感谢您提供有关 is_nothrow_destructible 的信息。它似乎做正确的事。您为什么要让它当前正确地报告不可破坏事物的“不可破坏”这一事实,让您将事物更改为可破坏?
  • @Xeo:你真的能控制实例化吗?如果你的类是一个基础,那么这意味着我可以编写自己的扩展,而你无法控制我如何允许用户实例化我的对象。
  • @DavidRodríguez-dribeas 最后,其他程序员总是可以随心所欲。您真正能做的最好的事情就是制定有据可查的政策和规避尝试的障碍。
猜你喜欢
  • 2010-10-03
  • 2012-09-01
  • 2021-11-06
  • 2023-02-01
  • 2011-03-30
  • 2011-03-03
  • 2020-12-01
  • 2011-10-21
  • 1970-01-01
相关资源
最近更新 更多