¿Necesito llamar explícitamente al destructor virtual base?

350

Al anular una clase en C ++ (con un destructor virtual), estoy implementando el destructor nuevamente como virtual en la clase heredada, pero ¿necesito llamar al destructor base?

Si es así, me imagino que es algo así ...

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

    // Some destructing specific to MyChildClass
}

Estoy en lo cierto?

Nick Bolton
fuente

Respuestas:

470

No, los destructores se llaman automáticamente en el orden inverso de construcción. (Las clases base duran). No llame a destructores de clase base.

Lou Franco
fuente
¿Qué pasa con los destructores virtuales puros? Mi enlazador está intentando llamarlo al final del destructor no virtual de mi clase heredada;
cjcurrie
40
no puedes tener un destructor virtual puro sin un cuerpo. Solo dale un cuerpo vacío. Con un método virtual puro regular, la función de anulación se llama en su lugar, con destructores, todos se llaman, por lo que debe proporcionar un cuerpo. El = 0 solo significa que debe ser anulado, por lo que sigue siendo una construcción útil si la necesita.
Lou Franco
1
Esta pregunta podría estar relacionada y ayudar a las preguntas / 15265106 / ca-missing-vtable-error .
Paul-Sebastian Manole
¿Por qué el código de Nick Bolton no causa una falla de segmentación aunque llama al destructor base dos veces, mientras que llamar deletea un puntero a la clase base dos veces causa una falla de segmentación?
Maggyero
2
No tiene garantizado un error de segmentación con ningún código incorrecto. Además, llamar a un destructor no libera memoria.
Lou Franco
92

No, no necesita llamar al destructor base, el destructor derivado siempre llama a un destructor base. Vea mi respuesta relacionada aquí para el orden de destrucción .

Para comprender por qué desea un destructor virtual en la clase base, consulte el código a continuación:

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


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

Cuando tu lo hagas:

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

Entonces, si no tuviera un destructor virtual en B, solo se llamaría ~ B (). Pero como tiene un destructor virtual, primero se llamará a ~ D (), luego a ~ B ().

Brian R. Bondy
fuente
20
Incluya la salida del programa (pseudo). Ayudará al lector.
Kuldeep Singh Dhaka
@KuldeepSinghDhaka El lector puede verlo en vivo en wandbox.org/permlink/KQtbZG1hjVgceSlO .
los cerdos el
27

Lo que dijeron los demás, pero también tenga en cuenta que no tiene que declarar el destructor virtual en la clase derivada. Una vez que declara un destructor virtual, como lo hace en la clase base, todos los destructores derivados serán virtuales, ya sea que los declare o no. En otras palabras:

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

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

struct C : public A {
   ~C() {}          // this is virtual too
};
Wodzu
fuente
1
¿Qué pasa si ~ B no se declara virtual? ¿~ C sigue siendo virtual?
Será
55
Si. Cuando un método virtual (cualquiera, no solo el destructor) se declara virtual, todas las anulaciones de ese método en las clases derivadas son automáticamente virtuales. En este caso, incluso si no declara ~ B virtual, todavía lo es, y también lo es ~ C.
boycy
1
Pero a diferencia de otros métodos anulados que tienen el mismo nombre y parámetros de sus métodos correspondientes en la clase base, el nombre del destructor es diferente. @boycy
Yuan Wen
1
@YuanWen no, el destructor derivado (uno y único) siempre anula el destructor (único) de su clase base.
boycy
10

No. A diferencia de otros métodos virtuales, donde llamaría explícitamente al método Base de Derivado para 'encadenar' la llamada, el compilador genera código para llamar a los destructores en el orden inverso en el que se llamaron sus constructores.

itsmatt
fuente
9

No, nunca se llama destructor de clase base, siempre se llama automáticamente como otros han señalado, pero aquí hay una prueba de concepto con resultados:

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;
}

El resultado es:

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 . . .

Si configura el destructor de clase base como virtual, cuál debería ser, entonces los resultados del caso 3 serían los mismos que los casos 1 y 2.

zar
fuente
Buena ilustracion. Si intenta llamar al destructor de la clase base desde la clase derivada, debería obtener un error del compilador similar a "error: no hay una función coincidente para la llamada a 'BASE :: BASE ()' <nueva línea> ~ BASE ();" Al menos este es el comportamiento de mi compilador g ++ 7.x.
Kemin Zhou
6

No. Se llama automáticamente.

Benoît
fuente
1

Los destructores en C ++ se invocan automáticamente en el orden de sus construcciones (Derivado luego Base) solo cuando se declara el destructor de clase Basevirtual .

De lo contrario, solo se invoca el destructor de la clase base en el momento de la eliminación del objeto.

Ejemplo: sin destructor 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;

}

Salida

Base Constructor
Derived Constructor
Base Destructor

Ejemplo: con Destructor virtual 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;

}

Salida

Base Constructor
Derived Constructor
Derived Destructor
Base Destructor

Se recomienda declarar el destructor de clase base ya que de lo virtualcontrario, causa un comportamiento indefinido.

Referencia: Destructor virtual

Adarsh ​​Kumar
fuente