为什么虚拟析构函数的行为方式如此?

时间:2012-10-22 20:16:39

标签: c++

我读到虚拟析构函数必须在具有虚方法的类中声明。我只是不明白为什么他们必须被宣布为虚拟。我知道为什么我们需要虚拟析构函数,如下例所示。我只是想知道为什么编译器不为我们管理虚拟析构函数。有关虚拟析构函数的工作需要了解的内容吗? 以下示例显示如果析构函数未声明为虚拟,则不会调用派生类的析构函数,为什么会这样?

class Base 
{
    // some virtual methods
public:
    Base()
    {std::cout << "Base Constructor\n";}
    ~Base()
    {std::cout << "Base De-structor\n";}

};

class Derived : public Base
{
public:
    Derived()
    {std::cout << "Der constructor\n";}
    ~Derived()
    { std::cout << "Der De-structor\n";}
} ;         
void main()
{

    Base *b = new Derived();
    delete b;
}

3 个答案:

答案 0 :(得分:4)

  

我只是想知道为什么编译器不为我们管理虚拟析构函数。

因为在C ++中,您需要为使用的内容付费。默认情况下,具有virtual析构函数涉及编译器向类添加虚拟表指针,这会增加其大小。这并不总是令人满意的。

  

以下示例显示如果析构函数未声明为虚拟,则不会调用派生类的析构函数,为什么会这样?

示例显示未定义的行为。这完全是违反规则的。并非所有析构函数都被调用的事实只是一种可能的表现形式。它可能会崩溃。

  

关于虚拟析构函数的工作,我需要了解一些内容吗?

是。如果您通过指向基类的指针删除对象,则它们是必需的。否则它是未定义的行为。

5.3.5删除[expr.delete]

  

3)在第一个备选(删除对象)中,如果要删除的对象的静态类型与其不同   动态类型,静态类型应该是要删除的对象的动态类型的基类,并且   static类型应具有虚拟析构函数或行为未定义。在第二种选择中(删除   array)如果要删除的对象的动态类型与其静态类型不同,则行为未定义。 (强调我的)

答案 1 :(得分:3)

  

我读到虚拟析构函数必须在具有虚方法的类中声明。

“必须”这句话过于强硬:“应该”更适合这种建议。

  

我只是想知道为什么编译器不为我们管理虚拟析构函数。

C ++设计人员试图避免编译器执行只在最极端情况下才要求它执行的操作。语言设计者认识到制作类多态的决定应由程序设计者决定,因此他们拒绝将此责任重新分配给编译器。

  

以下示例显示如果析构函数未声明为虚拟,则不会调用派生类的析构函数,为什么会这样?

因为您的代码无效:通过声明Derived非虚拟的析构函数,您承诺永远不会通过指向Derived的指针销毁Base;你的main打破了这个承诺,调用了未定义的行为。

请注意,仅通过声明具有确切类型的b变量,您就可以避免与非虚拟析构函数关联的问题(link to ideone)。但是,这会导致设计相当不稳定,因此您应该避免使用虚函数和非虚析构函数的继承层次结构。

答案 2 :(得分:3)

  

我读到虚拟析构函数必须在具有虚方法的类中声明。

是。但这太过于简单化了 它不是具有虚方法的类需要虚拟析构函数。但是使用虚方法的类的方式意味着它通常需要一个虚拟析构函数。如果通过指向其基类的指针删除对象,则需要虚拟析构函数。问题是,当一个对象具有虚拟方法时,你通常使用指向其基类的指针,即使实际对象略有不同。

  

我无法理解为什么必须将它们声明为虚拟。

并非他们必须这样做。如上所述。这是通常使用模式的结果。

  

我只是想知道为什么编译器不为我们管理虚拟析构函数。

因为并不总是需要它。 C ++的精神是你不需要为你不需要的东西买单。如果编译器总是将虚拟析构函数添加到具有虚方法的类中,那么即使在我可以在我的代码库中证明我不需要它的情况下,我也必须付出使用虚拟析构函数的代价。

  

我是否需要了解虚拟析构函数的工作?

使用它们只需要很少的费用。

  

如果析构函数未声明为虚拟,则不会调用派生类的析构函数,为什么会这样?

这就是我们有虚拟析构函数导致此行为的原因。如果需要此行为,则需要添加虚拟析构函数。但有些情况下可能不需要虚拟析构函数,这允许这种方法的用户不付出代价。

相关问题