ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

    // Some destructing specific to MyChildClass
}

我对吗?


L
Lou Franco

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


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

不,您不需要调用基析构函数,派生析构函数始终为您调用基析构函数。 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()。


请包括程序(伪)输出。它将帮助读者。
@KuldeepSinghDhaka 读者可以在 wandbox.org/permlink/KQtbZG1hjVgceSlO 现场观看。
W
Wodzu

其他人所说的,但还要注意,您不必在派生类中声明析构函数 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 不会,(唯一的)派生析构函数总是覆盖其基类的(唯一的)析构函数。
A
Adarsh Kumar

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

示例:使用 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


这是关于虚拟或非虚拟的非常重要的说明。感谢您提供此答案。
M
Michel Keijzers

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

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 编译器的行为。
i
itsmatt

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


B
Benoît

不,它是自动调用的。