Pregunta Cuándo usar destructores virtuales?


Tengo una sólida comprensión de la mayoría de las teorías de OO, pero una de las cosas que más me confunde son los destructores virtuales.

Pensé que siempre se llama al destructor sin importar qué y para cada objeto en la cadena.

¿Cuándo se supone que debes hacerlos virtuales y por qué?


1206
2018-01-20 12:58


origen


Respuestas:


Los destructores virtuales son útiles cuando puede eliminar una instancia de una clase derivada a través de un puntero a la clase base:

class Base 
{
    // some virtual methods
};

class Derived : public Base
{
    ~Derived()
    {
        // Do some important cleanup
    }
};

Aquí, notarás que no declare que el destructor de Base sea virtual. Ahora, echemos un vistazo al siguiente fragmento:

Base *b = new Derived();
// use b
delete b; // Here's the problem!

Como el destructor de Base no es virtual y b es un Base* apuntando a un Derived objeto, delete b tiene comportamiento indefinido:

[En delete b], si el tipo estático del   el objeto a eliminar es diferente de su tipo dinámico, el estático   tipo será una clase base del tipo dinámico del objeto que se va a   eliminado y el tipo estático tendrá un destructor virtual o el   el comportamiento no está definido.

En la mayoría de las implementaciones, la llamada al destructor se resolverá como cualquier código no virtual, lo que significa que se llamará al destructor de la clase base, pero no al de la clase derivada, lo que generará una pérdida de recursos.

En resumen, siempre crea destructores de clases base virtual cuando están destinados a ser manipulados polimórficamente.

Si desea evitar la eliminación de una instancia a través de un puntero de clase base, puede hacer que el destructor de la clase base esté protegido y no sea virtual; al hacerlo, el compilador no te dejará llamar delete en un puntero de clase base.

Puede obtener más información sobre la virtualidad y el destructor de la clase base virtual en este artículo de Herb Sutter.


1310
2018-01-20 13:04



Declarar destructores virtuales en clases base polimórficas. Este es el artículo 7 en Scott Meyers ' Eficaz C ++. Meyers continúa para resumir que si una clase tiene alguna función virtual, debería tener un destructor virtual, y que las clases no diseñadas para ser clases base o no diseñadas para ser utilizadas polimórficamente deberían no declarar destructores virtuales.


165
2018-01-20 13:11



Un constructor virtual no es posible, pero el destructor virtual es posible. Vamos a experimentar ...

#include <iostream>

using namespace std;

class Base
{
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    ~Base(){
        cout << "Base Destructor called\n";
    }
};

class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    Base *b = new Derived1();
    delete b;
}

El código anterior muestra lo siguiente:

Base Constructor Called
Derived constructor called
Base Destructor called

La construcción del objeto derivado sigue la regla de construcción, pero cuando eliminamos el puntero "b" (puntero base), hemos encontrado que solo se llama al destructor base. Pero esto no debe suceder. Para hacer lo correcto, tenemos que hacer que el destructor base sea virtual. Ahora veamos qué sucede en lo siguiente:

#include <iostream>

using namespace std;

class Base
{ 
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    virtual ~Base(){
        cout << "Base Destructor called\n";
    }
};

class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    Base *b = new Derived1();
    delete b;
}

El resultado cambió de la siguiente manera:

Base Constructor Called
Derived constructor called
Derived destructor called
Base Destructor called

Entonces, la destrucción del puntero base (que toma una asignación en el objeto derivado) sigue la regla de destrucción, es decir, primero la derivada y luego la base. Por otro lado, para el constructor no hay nada como el constructor virtual.


153
2018-04-09 13:39



También tenga en cuenta que eliminar un puntero de clase base cuando no hay un destructor virtual dará como resultado comportamiento indefinido. Algo que aprendí hace poco:

¿Cómo se debe comportar la eliminación de anulación en C ++?

He estado usando C ++ durante años y todavía me las arreglé para ahorcarme.


37
2018-01-21 01:09



Haga el destructor virtual cada vez que su clase sea polimórfica.


30
2018-01-20 13:01



Llamar destructor a través de un puntero a una clase base

struct Base {
  virtual void f() {}
  virtual ~Base() {}
};

struct Derived : Base {
  void f() override {}
  ~Derived() override {}
};

Base* base = new Derived;
base->f(); // calls Derived::f
base->~Base(); // calls Derived::~Derived

La llamada a un destructor virtual no es diferente de cualquier otra llamada a función virtual.

por base->f(), la llamada será enviada a Derived::f()y es lo mismo base->~Base() - su función predominante - el Derived::~Derived() sera llamado.

Lo mismo ocurre cuando se llama al destructor de forma indirecta, p. delete base;. los delete declaración llamará base->~Base() que será enviado a Derived::~Derived().

Clase abstracta con destructor no virtual

Si no va a eliminar el objeto a través de un puntero a su clase base, entonces no es necesario tener un destructor virtual. Solo hazlo protected para que no se llame accidentalmente:

// library.hpp

struct Base {
  virtual void f() = 0;

protected:
  ~Base() = default;
};

void CallsF(Base& base);
// CallsF is not going to own "base" (i.e. call "delete &base;").
// It will only call Base::f() so it doesn't need to access Base::~Base.

//-------------------
// application.cpp

struct Derived : Base {
  void f() override { ... }
};

int main() {
  Derived derived;
  CallsF(derived);
  // No need for virtual destructor here as well.
}

10
2018-05-18 13:38



Me gusta pensar en interfaces e implementaciones de interfaces. En C ++, la interfaz de habla es pura clase virtual. Destructor es parte de la interfaz y se espera que se implemente. Por lo tanto, destructor debe ser puramente virtual. ¿Qué hay de constructor? Constructor en realidad no es parte de la interfaz porque el objeto siempre se crea una instancia de forma explícita.


7
2017-11-08 16:28



Para ser simple, El destructor virtual debe destruir los recursos en el orden correcto, cuando elimina un puntero de clase base que apunta al objeto de clase derivado.

 #include<iostream>
 using namespace std;
 class B{
    public:
       B(){
          cout<<"B()\n";
       }
       virtual ~B(){ 
          cout<<"~B()\n";
       }
 };
 class D: public B{
    public:
       D(){
          cout<<"D()\n";
       }
       ~D(){
          cout<<"~D()\n";
       }
 };
 int main(){
    B *b = new D();
    delete b;
    return 0;
 }

OUTPUT:
B()
D()
~D()
~B()

==============
If you don't give ~B()  as virtual. then output would be 
B()
D()
~B()
where destruction of ~D() is not done which leads to leak


6
2017-08-26 05:33



La palabra clave virtual para destructor es necesaria cuando desee que diferentes destructores sigan el orden correcto mientras los objetos se eliminan a través del puntero de la clase base. por ejemplo:

Base *myObj = new Derived();
// Some code which is using myObj object
myObj->fun();
//Now delete the object
delete myObj ; 

Si su destructor de clase derivado es virtual, los objetos se destruntarán en un orden (primero el objeto derivado y luego la base). Si su destructor de clase derivado NO es virtual, entonces solo el objeto de clase base será eliminado (porque el puntero es de clase base "Base * myObj"). Por lo tanto, habrá pérdida de memoria para el objeto derivado.


5
2018-01-29 07:11