为什么[C++]默认情况下析构函数不是虚函数?

32

为什么C++不会默认将至少有一个其他虚函数的类的析构函数设置为虚函数?在这种情况下,添加虚析构函数对我来说没有成本,而不拥有虚析构函数几乎总是一个错误。 C ++0x会解决这个问题吗?


2
关键字是“almost”。如果您的基类有虚函数,而您不想为虚析构函数付费,那么在这个新世界中如何指定它不是虚拟的?另外,所有旧代码会发生什么?我们需要一个处理向后兼容性问题的计划。 - Martin York
4
虚析构函数是有成本的,因为它需要为所有派生类复制另一个析构函数代码。请参见此问题 - Simon Richter
可能是为什么不将所有函数都设为虚函数的重复问题。 - iammilind
@Simon,D0变体可以作为D1的包装器来实现。 - bdonlan
1
不是重复问题,我明白为什么不是所有函数默认都是虚函数。我的问题是,为什么C++不会在你声明另一个函数为虚函数时默认将析构函数设为虚函数。 - Jeff Linahan
3个回答

22
你不需要的部分无需付费。如果你从未通过基础指针删除,则可能不希望间接调用析构函数的开销。
也许你认为虚表的存在是唯一的开销。但是每个单独的函数调度也必须考虑到,如果我想直接分派我的析构函数调用,我应该被允许这样做。
如果你确实删除了一个基础指针并且该类有虚方法,编译器警告你会很好,我想。
编辑:让我们引入Simon的优秀评论:请查看此SO问题中生成的析构函数代码。正如你所看到的,还要考虑代码膨胀的开销。

3
以下是一个例子(我并不建议编写这样的代码):
struct base {
    virtual void foo() const = 0;
    virtual void bar () const = 0;
};

struct derived: base {
    void foo() const {}
    void bar() const {}
};

std::shared_ptr<base>
make_base()
{
    return std::make_shared<derived>();
}

这是一段没有展示UB(未定义行为)的完美代码。这是因为std::shared_ptr使用类型擦除;即使最后一个触发销毁的std::shared_ptr类型是std::shared_ptr<void>,最终调用delete将删除一个derived*
请注意,std::shared_ptr的这种行为并非专门针对虚拟析构函数而设计;它还有其他各种用途(例如std::shared_ptr<FILE> { std::fopen( ... ), std::fclose })。然而,由于这种技术已经支付了一些间接性的成本,因此一些用户可能不感兴趣为他们的基类提供虚拟析构函数。这就是“只为所需付费”的意思。

2
按照标准的要求,一个非虚析构函数的多态类并不是一个错误。在对这样的对象执行某些特定操作时会导致未定义的行为,但其他一切都是完全合法的。因此,在标准允许程序员犯错的方面表现得宽容的情况下,为什么需要对析构函数进行特殊处理呢?
这样的改变会有成本,尽管大部分都是微不足道的:虚表将增加一个元素,并且与析构函数相关的虚派发。
据我所知,C++11中在这方面没有对析构函数的行为进行任何更改。我想在特殊成员函数的章节中应该会有说明,但实际上没有,同样在一般的虚函数章节中也没有。

是否曾经有计划在C++0x中使虚拟析构函数成为默认值?我这样问是因为我发现了这篇论文:http://www2.research.att.com/~bs/C++0x_panel.pdf,在底部提到了这一点,但我没有找到更多的相关信息。 - Jeff Linahan
1
@dacode:我没有参加实际的会议等活动,所以我不知道。在这四个“尴尬”的事情中,只有最后一个实际上被改变了。如果有人提交了这样的更改提案,我不会感到惊讶,但为什么它被否决了,我无法说。 - Dennis Zickefoose

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接