¿Cuándo usar destructores virtuales?

11 minutos de lectura

¿Cuando usar destructores virtuales
Lodle

Tengo una sólida comprensión de la mayoría OOP teoría, pero lo único que me confunde mucho son los destructores virtuales.

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

¿Cuándo está destinado a hacerlos virtuales y por qué?

  • Ver esto: Destructor virtual

    – Naveen

    20 de enero de 2009 a las 13:04

  • cada destructor abajo recibe llamadas pase lo que pase. virtual asegúrese de que comience en la parte superior en lugar de en el medio.

    – Pato mugido

    29 de junio de 2013 a las 0:32

  • pregunta relacionada: ¿Cuándo no debería usar destructores virtuales?

    – Eitan T.

    4 de agosto de 2013 a las 16:39

  • También estoy confundido por la respuesta de @MooingDuck. ¿No debería ser arriba en lugar de abajosi usa la noción de subclase (debajo) y superclase (arriba)?

    – Nibor

    20 de junio de 2019 a las 9:31


  • @Nibor: Sí, si usas esa noción. Aproximadamente la mitad de las personas con las que hablo ven las superclases como “arriba”, y la mitad las ven como “abajo”, por lo que ambos son estándares en conflicto, lo que hace que todo sea confuso. Creo que la superclase como “arriba” es un poco más común, pero esa no es la forma en que me enseñaron 🙁

    – Pato mugido

    20 de junio de 2019 a las 17:09

1647656470 103 ¿Cuando usar destructores virtuales
Lucas Touraille

Los destructores virtuales son útiles cuando potencialmente podría 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 declaré que el destructor de Base fuera virtual. Ahora, echemos un vistazo al siguiente fragmento:

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

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

[In delete b]si el tipo estático del objeto a eliminar es diferente de su tipo dinámico, el tipo estático será una clase base del tipo dinámico del objeto a eliminar y el tipo estático tendrá un destructor virtual o 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 provocará una fuga de recursos.

En resumen, siempre haga 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 clase base esté protegido y no virtual; al hacerlo, el compilador no le permitirá llamar delete en un puntero de clase base.

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

  • Esto explicaría por qué tuve fugas masivas usando una fábrica que hice antes. Todo tiene sentido ahora. Gracias

    – Lodle

    20 de enero de 2009 a las 13:08

  • Bueno, este es un mal ejemplo ya que no hay miembros de datos. Y si Base y Derived tener todos variables de almacenamiento automático? es decir, no hay código personalizado “especial” o adicional para ejecutar en el destructor. ¿Está bien dejar de escribir destructores? ¿O la clase derivada todavía ¿Tienes una pérdida de memoria?

    – bobobobo

    8 de julio de 2012 a las 18:27

  • Espera, será un comportamiento indefinido.

    – bobobobo

    8 de julio de 2012 a las 18:29

  • Del artículo de Herb Sutter: “Pauta n.º 4: un destructor de clase base debe ser público y virtual, o protegido y no virtual”.

    – Helado

    9 de febrero de 2016 a las 8:22

  • También del artículo: ‘si eliminas polimórficamente sin un destructor virtual, invocas el temido espectro del “comportamiento indefinido”, un espectro que personalmente preferiría no encontrar ni siquiera en un callejón moderadamente bien iluminado, muchas gracias.’ jajaja

    – Bondolina

    29 de febrero de 2016 a las 14:30

¿Cuando usar destructores virtuales
Tunvir Rahman Tusher

No es posible un constructor virtual, pero sí un destructor virtual. Experimentemos…….

#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 genera 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), encontramos 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;
}

La salida 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 el Derivado, luego la Base. Por otro lado, no hay nada como un constructor virtual.

  • “El constructor virtual no es posible” significa que no necesita escribir un constructor virtual por su cuenta. La construcción del objeto derivado debe seguir la cadena de construcción desde la derivada hasta la base. Por lo tanto, no necesita escribir la palabra clave virtual para su constructor. Gracias

    – Tunvir Rahman Tusher

    19 de abril de 2013 a las 6:50

  • @Murkantilism, “los constructores virtuales no se pueden hacer” es cierto. Un constructor no se puede marcar como virtual.

    – cmeub

    21 de abril de 2013 a las 20:09

  • @cmeub, pero hay un modismo para lograr lo que querrías de un constructor virtual. Ver parashift.com/c++-faq-lite/virtual-ctors.html

    – cabo1232

    3 de octubre de 2013 a las 12:58

  • @TunvirRahmanTusher, ¿podría explicar por qué se llama Base Destructor?

    – Rimalroshan

    11 de noviembre de 2017 a las 8:49

  • @rimiro Es automático por c ++. Puede seguir el enlace stackoverflow.com/questions/677620/…

    – Tunvir Rahman Tusher

    11 de noviembre de 2017 a las 12:01

Declare destructores virtuales en clases base polimórficas. Este es el artículo 7 en el C++ efectivo de Scott Meyers. Meyers continúa resumiendo que si una clase tiene ninguna 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 usarse polimórficamente deberían no declarar destructores virtuales.

  • +”Si una clase tiene alguna función virtual, debe tener un destructor virtual, y las clases que no están diseñadas para ser clases base o que no están diseñadas para usarse polimórficamente no deben declarar destructores virtuales”.: ¿Hay casos en los que tiene sentido romper esta regla? Si no, ¿tendría sentido que el compilador verifique esta condición y emita un error si no se cumple?

    – Jorge

    6 de mayo de 2012 a las 9:29

  • @Giorgio No conozco ninguna excepción a la regla. Pero no me calificaría a mí mismo como un experto en C++, por lo que es posible que desee publicar esto como una pregunta separada. Una advertencia del compilador (o una advertencia de una herramienta de análisis estático) tiene sentido para mí.

    – Bill el lagarto

    6 mayo 2012 a las 13:08

  • Las clases se pueden diseñar para que no se eliminen a través del puntero de un determinado tipo, y aún así tener funciones virtuales; un ejemplo típico es una interfaz de devolución de llamada. Uno no elimina su implementación a través de un puntero de interfaz de devolución de llamada, ya que es solo para suscribirse, pero tiene funciones virtuales.

    – dascandy

    15 de enero de 2016 a las 5:05

  • @dascandy Exactamente, eso o todos los muchos otras situaciones en las que usamos un comportamiento polimórfico pero no realizamos la administración del almacenamiento a través de punteros, por ejemplo, manteniendo objetos automáticos o de duración estática, con punteros que solo se usan como rutas de observación. No hay necesidad/propósito en la implementación de un destructor virtual en tales casos. Dado que solo estamos citando a personas aquí, prefiero Sutter de arriba: “Pauta n. ° 4: un destructor de clase base debe ser público y virtual, o protegido y no virtual”. Este último garantiza que cualquier persona que intente eliminar accidentalmente a través de un puntero base muestre el error de sus formas

    – subrayado_d

    23 de abril de 2016 a las 15:58


  • @Giorgio En realidad, hay un truco que uno puede usar y evitar una llamada virtual a un destructor: vincular a través de una referencia constante un objeto derivado a una base, como const Base& = make_Derived();. En este caso, el destructor del Derived Se llamará a prvalue, incluso si no es virtual, por lo que se ahorra la sobrecarga introducida por vtables/vpointers. Por supuesto, el alcance es bastante limitado. Andrei Alexandrescu mencionó esto en su libro Diseño moderno en C++.

    – vsoftco

    2 de noviembre de 2016 a las 20:06


1647656471 596 ¿Cuando usar destructores virtuales
Sándwich grande

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

¿Cómo debería comportarse la anulación de eliminar en C++?

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

1647656471 562 ¿Cuando usar destructores virtuales
yesraaj

Haz que el destructor sea virtual siempre que tu clase sea polimórfica.

1647656472 442 ¿Cuando usar destructores virtuales
Abix

Llamar a 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 de destructor virtual no es diferente de cualquier otra llamada de función virtual.

Para base->f()la llamada se enviará a Derived::f()y es lo mismo para base->~Base() – su función principal – el Derived::~Derived() sera llamado.

Lo mismo sucede cuando se llama a destructor indirectamente, por ejemplo 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 un 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.
}

1647656472 719 ¿Cuando usar destructores virtuales
Prakash GiBB

Para ser simple, Virtual destructor destruye los recursos en el orden correcto, cuando elimina un puntero de clase base que apunta a un 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

  • No tener el destructor virtual base y llamando delete en un puntero base conduce a un comportamiento indefinido.

    –James Adkison

    13/10/2016 a las 20:00

  • @JamesAdkison, ¿por qué conduce a un comportamiento indefinido?

    – Rimalroshan

    11 de noviembre de 2017 a las 11:06


  • @rimiro Es lo que dice la norma. No tengo una copia, pero el enlace lo lleva a un comentario donde alguien hace referencia a la ubicación dentro del estándar.

    –James Adkison

    11 de noviembre de 2017 a las 13:52


  • @rimiro “Si la eliminación, por lo tanto, se puede realizar polimórficamente a través de la interfaz de clase base, entonces debe comportarse virtualmente y debe ser virtual. De hecho, el lenguaje lo requiere: si elimina polimórficamente sin un destructor virtual, invoca el temido espectro de “comportamiento indefinido”, un espectro que personalmente preferiría no encontrar ni siquiera en un callejón moderadamente bien iluminado, muchas gracias”. (gotw.ca/publications/mill18.htm) — Sutter de hierbas

    –James Adkison

    11 de noviembre de 2017 a las 13:54

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad