删除派生对象时是否始终会调用基础析构函数?

时间:2010-11-08 17:04:46

标签: c++

说我有以下内容:

class Foo {
public:
void destroy();
~Foo()
{
   destroy();
}
};

class Bar : public Foo {
public:
~Bar() {}
};

如果我有一个Bar对象,当对象栏被删除时会调用Foo的析构函数吗?

由于

5 个答案:

答案 0 :(得分:16)

但上述代码很危险;如果您未将~Foo()声明为virtual,则如果您通过{{1}在~Bar()对象上调用delete,则不会调用Bar }}

答案 1 :(得分:5)

是的,如果您持有Bar参考,这将有效。然而,这是一个非常脆弱的解决方案,因为它不相反(持有Foo&Foo*指向Bar)。为了使其工作Foo,析构函数必须是虚拟的。否则它将执行非虚拟调度,并仅在Foo上调用析构函数。

class Foo { 
public: 
void destroy(); 
virtual ~Foo() 
{ 
   destroy(); 
} 
}; 

class Bar : public Foo { 
public: 
virtual ~Bar() {} 
}; 

在预期继承的一般情况下,涉及的类型应使用虚拟析构函数进行标记。像大多数规则一样,尽管100%的情况并非如此。但是,不要在这里涉及所有案例,我鼓励您阅读以下文章中的问题#2(感谢Steve的链接)

答案 2 :(得分:2)

如果派生类可能包含任何重要内容,则使基类的析构函数为虚拟。事实上,如果有人从你的班级中获得任何机会,你应该把它变成虚拟的。

无论如何,如果你实现了析构函数,你还应该实现Big-3:

这里有一些代码可以使虚拟析构函数的行为变得明显:

#include <iostream>

class Base1
{
public:
  ~Base1() // Uh-oh, not virtual...
  {
    std::cout << "~Base1()\n";
  }
};

class Derived1 : public Base1
{
public:
  ~Derived1()
  {
    std::cout << "~Derived1()\n";
  }
};

class Base2
{
public:
  virtual ~Base2() // Good, it's virtual...
  {
    std::cout << "~Base2()\n";
  }
};

class Derived2 : public Base2
{
public:
  ~Derived2()
  {
    std::cout << "~Derived2()\n";
  }
};

int main()
{
  std::cout << "Simple Base1: ";
  Base1* b1 = new Base1();
  delete b1;

  std::cout << "Simple Base2: ";
  Base2* b2 = new Base2();
  delete b2;

  std::cout << "Simple Derived1: ";
  Derived1* d1 = new Derived1();
  delete d1;

  std::cout << "Simple Derived2: ";
  Derived2* d2 = new Derived2();
  delete d2;

  std::cout << "Polymorphic Derived1: ";
  b1 = new Derived1();
  delete b1;

  std::cout << "Polymorphic Derived2: ";
  b2 = new Derived2();
  delete b2;

  return 0;
}
  

简单Base1:~Base1()   
简单Base2:~Base2()   
Simple Derived1:~Derived1()   
〜基础1()   
Simple Derived2:~Derived2()   
〜和Base2()   
多态Derived1:~Base1()注意 - 我在另一个答案中读到这个行为是未定义的,但这也差一点,如果不是更糟的话。基本上,确保您的基类具有虚拟析构函数   
多态Derived2:~Derived2()   
〜和Base2()

答案 3 :(得分:0)

我在这里会有点迂腐并说“是的,但有时候答案是否定的”。例如:

class Foo {
public:
  void destroy();
  ~Foo()
  {
    destroy();
  }
};

class Bar : public Foo {
public:
  ~Bar() { exit (0); } // code never returns from exit call so base class
                       // destructor never executed!
};

抛出异常也可能会中断正常的析构函数序列。此外,析构函数中的任何未定义行为都将消除调用基类析构函数的保证(因为任何事情都可能发生)。

但通常,在派生类析构函数完成后调用基类析构函数。

答案 4 :(得分:-5)

您需要在基类中声明析构函数“virtual”以获得该行为。