【问题标题】:Do I need to explicitly call the base virtual destructor?我需要显式调用基本虚拟析构函数吗?
【发布时间】:2010-10-15 05:24:30
【问题描述】:

当在 C++ 中重写一个类时(使用虚拟析构函数),我在继承类上再次将析构函数实现为虚拟,但我需要调用基析构函数吗?

如果是这样,我想它是这样的......

MyChildClass::~MyChildClass() // virtual in header
{
    // Call to base destructor...
    this->MyBaseClass::~MyBaseClass();

    // Some destructing specific to MyChildClass
}

我说的对吗?

【问题讨论】:

    标签: c++ destructor


    【解决方案1】:

    不,析构函数会以与构造相反的顺序自动调用。 (最后是基类)。不要调用基类析构函数。

    【讨论】:

    • 纯虚析构函数呢?我的链接器试图在我继承类的非虚拟析构函数的末尾调用它;
    • 你不能有一个没有身体的纯虚拟析构函数。只是给它一个空的身体。使用常规的纯虚方法,调用覆盖函数,使用析构函数,它们都被调用,所以你必须提供一个主体。 =0 只是意味着它必须被覆盖,所以如果你需要它仍然是一个有用的结构。
    • 这个问题可能与questions/15265106/c-a-missing-vtable-error有关和帮助。
    • 为什么 Nick Bolton 的代码虽然调用了基析构函数两次,却不会导致分段错误,而在指向基类的指针上调用 delete 两次却会导致分段错误?
    • 不保证任何错误代码的分段错误。此外,调用析构函数不会释放内存。
    【解决方案2】:

    不,您不需要调用基析构函数,派生析构函数始终会为您调用基析构函数。 Please see my related answer here for order of destruction

    要了解为什么要在基类中使用虚拟析构函数,请查看以下代码:

    class B
    {
    public:
        virtual ~B()
        {
            cout<<"B destructor"<<endl;
        }
    };
    
    
    class D : public B
    {
    public:
        virtual ~D()
        {
            cout<<"D destructor"<<endl;
        }
    };
    

    当你这样做时:

    B *pD = new D();
    delete pD;
    

    如果你在 B 中没有虚拟析构函数,则只会调用 ~B()。但是由于你有一个虚拟析构函数,所以首先调用 ~D(),然后调用 ~B()。

    【讨论】:

    【解决方案3】:

    其他人所说的,但也请注意,您不必在派生类中声明析构函数 virtual 。一旦你声明了一个虚拟的析构函数,就像你在基类中所做的那样,所有派生的析构函数都将是虚拟的,无论你是否声明它们。换句话说:

    struct A {
       virtual ~A() {}
    };
    
    struct B : public A {
       virtual ~B() {}   // this is virtual
    };
    
    struct C : public A {
       ~C() {}          // this is virtual too
    };
    

    【讨论】:

    • 如果 ~B 没有被声明为虚拟怎么办? ~C 还是虚拟的吗?
    • 是的。当一个虚拟方法(任何,不仅仅是析构函数)被声明为虚拟时,派生类中该方法的所有覆盖都是自动虚拟的。在这种情况下,即使你不声明 ~B 是虚拟的,它仍然是虚拟的, ~C 也是如此。
    • 但是不像其他被覆盖的方法在基类中对应的方法名和参数一样,析构函数名不一样。有关系吗? @boycy
    • @YuanWen 不,它不会,(唯一的)派生析构函数总是覆盖其基类的(唯一的)析构函数。
    【解决方案4】:

    不,您从不调用基类析构函数,它总是像其他人指出的那样自动调用,但这里是概念证明和结果:

    class base {
    public:
        base()  { cout << __FUNCTION__ << endl; }
        ~base() { cout << __FUNCTION__ << endl; }
    };
    
    class derived : public base {
    public:
        derived() { cout << __FUNCTION__ << endl; }
        ~derived() { cout << __FUNCTION__ << endl; } // adding call to base::~base() here results in double call to base destructor
    };
    
    
    int main()
    {
        cout << "case 1, declared as local variable on stack" << endl << endl;
        {
            derived d1;
        }
    
        cout << endl << endl;
    
        cout << "case 2, created using new, assigned to derive class" << endl << endl;
        derived * d2 = new derived;
        delete d2;
    
        cout << endl << endl;
    
        cout << "case 3, created with new, assigned to base class" << endl << endl;
        base * d3 = new derived;
        delete d3;
    
        cout << endl;
    
        return 0;
    }
    

    输出是:

    case 1, declared as local variable on stack
    
    base::base
    derived::derived
    derived::~derived
    base::~base
    
    
    case 2, created using new, assigned to derive class
    
    base::base
    derived::derived
    derived::~derived
    base::~base
    
    
    case 3, created with new, assigned to base class
    
    base::base
    derived::derived
    base::~base
    
    Press any key to continue . . .
    

    如果您将基类析构函数设置为虚函数,则情况 3 的结果将与情况 1 和 2 相同。

    【讨论】:

    • 很好的插图。如果您尝试从派生类调用基类析构函数,您应该会收到类似于“错误:没有匹配函数调用‘BASE::BASE()’ ~BASE();”的编译器错误。至少这是我的 g++ 7.x 编译器的行为。
    【解决方案5】:

    C++ 中的析构函数自动被调用按照它们的构造顺序(Derived 然后 Base)仅当 Base 类析构函数声明为 virtual

    如果不是,则在删除对象时仅调用基类析构函数。

    示例:没有虚拟析构函数

    #include <iostream>
    
    using namespace std;
    
    class Base{
    public:
      Base(){
        cout << "Base Constructor \n";
      }
    
      ~Base(){
        cout << "Base Destructor \n";
      }
    
    };
    
    class Derived: public Base{
    public:
      int *n;
      Derived(){
        cout << "Derived Constructor \n";
        n = new int(10);
      }
    
      void display(){
        cout<< "Value: "<< *n << endl;
      }
    
      ~Derived(){
        cout << "Derived Destructor \n";
      }
    };
    
    int main() {
    
     Base *obj = new Derived();  //Derived object with base pointer
     delete(obj);   //Deleting object
     return 0;
    
    }
    

    输出

    Base Constructor
    Derived Constructor
    Base Destructor
    

    示例:使用 Base 虚拟析构函数

    #include <iostream>
    
    using namespace std;
    
    class Base{
    public:
      Base(){
        cout << "Base Constructor \n";
      }
    
      //virtual destructor
      virtual ~Base(){
        cout << "Base Destructor \n";
      }
    
    };
    
    class Derived: public Base{
    public:
      int *n;
      Derived(){
        cout << "Derived Constructor \n";
        n = new int(10);
      }
    
      void display(){
        cout<< "Value: "<< *n << endl;
      }
    
      ~Derived(){
        cout << "Derived Destructor \n";
        delete(n);  //deleting the memory used by pointer
      }
    };
    
    int main() {
    
     Base *obj = new Derived();  //Derived object with base pointer
     delete(obj);   //Deleting object
     return 0;
    
    }
    

    输出

    Base Constructor
    Derived Constructor
    Derived Destructor
    Base Destructor
    

    建议将基类析构函数声明为virtual,否则会导致未定义的行为。

    参考:Virtual Destructor

    【讨论】:

      【解决方案6】:

      没有。与其他虚拟方法不同,您可以从 Derived 显式调用 Base 方法以“链接”调用,编译器生成代码以按照调用构造函数的相反顺序调用析构函数。

      【讨论】:

        【解决方案7】:

        没有。它是自动调用的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-08-02
          • 2023-02-01
          • 2014-10-02
          • 2011-02-03
          • 1970-01-01
          • 1970-01-01
          • 2015-12-15
          • 2011-03-30
          相关资源
          最近更新 更多