我是否需要显式调用基本虚拟析构函数?


350

在C ++中(使用虚拟析构函数)覆盖类时,我在继承类上再次将析构函数实现为虚拟形式,但是我是否需要调用基本析构函数?

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

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

    // Some destructing specific to MyChildClass
}

我对吗?

Answers:


469

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


纯虚拟析构函数呢?我的链接器试图在继承的类的非虚拟析构函数的末尾调用它;
cjcurrie 2013年

40
没有身体就无法拥有纯粹的虚拟析构函数。只是给它一个空的身体。使用常规的纯虚方法,则调用覆盖函数,而使用析构函数,它们都被调用,因此必须提供一个主体。= 0意味着它必须被覆盖,因此如果需要它仍然是一个有用的构造。
Lou Franco


尽管Nick Bolton的代码两次调用了基本析构函数,但是为什么两次调用delete基类的指针却不会导致分段错误,所以为什么它不会引起分段错误呢?
Maggyero '18年

2
不保证您使用任何错误的代码来实现分段错误。同样,调用析构函数不会释放内存。
Lou Franco

92

不,您不需要调用基本析构函数,派生析构函数总是为您调用基本析构函数。 请查看我的相关答复,以了解销毁顺序

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

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()。


20
请包括程序(伪)输出。它将帮助读者。
Kuldeep Singh Dhaka

@KuldeepSinghDhaka读者可以在wandbox.org/permlink/KQtbZG1hjVgceSlO上实时查看它。

27

其他人说的话,但也请注意,您不必在派生类中将析构函数声明为虚拟。像在基类中一样声明了虚拟的析构函数之后,无论您是否声明它们,所有派生的析构函数都将是虚拟的。换一种说法:

struct A {
   virtual ~A() {}
};

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

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

1
如果〜B未声明为虚拟怎么办?〜C仍然是虚拟的吗?
威尔

5
是。当将虚拟方法(不是析构函数的任何方法)声明为虚拟方法时,派生类中该方法的所有替代都会自动成为虚拟方法。在这种情况下,即使您不声明〜B虚拟,它也仍然是,〜C也是如此。
boycy 2011年

1
但是与基类中其他具有相同名称和其对应方法的参数的重写方法不同,析构函数名称不同,这有关系吗?@boycy
袁雯

1
@YuanWen不,不会的,(唯一的)派生析构函数始终会覆盖其基类的(唯一的)析构函数。
boycy,2016年

10

不会。与其他虚拟方法不同,在其他虚拟方法中,您将从派生对象显式调用Base方法来“链接”调用,编译器生成代码以按与调用其构造函数相反的顺序来调用这些析构函数。


9

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

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()'<newline>〜BASE();”。至少这是我的g ++ 7.x编译器的行为。
Kemin Zhou


1

仅当声明了基类析构函数时,C ++ 中的析构函数才会按其构造顺序(先导出,然后继承)自动调用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

示例:使用基本虚拟析构函数

#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否则会导致未定义的行为。

参考:虚拟析构函数

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.