2010-01-14 8 views
20

¿Necesitamos un destructor virtual si mis clases no asignan ninguna memoria dinámicamente?Destructor virtual: ¿es necesario cuando no se asigna dinámicamente la memoria?

p. Ej.

class A 
{ 
     private: 
     int a; 
     int b; 

     public: 
     A(); 
     ~A(); 
}; 

class B: public A 
{  
     private: 
     int c; 
     int d; 

     public: 
     B(); 
     ~B(); 
}; 

En este caso, ¿necesitamos marcar el destructor de A como virtual?

+0

@ALL He ejecutado el ejemplo de código anterior con valgrind, al crear el objeto de clase derivado dinámicamente (es decir, "A * a = new B; delete a;") Observé que no hay pérdida de memoria. Incluso el destructor de clase derivado no se invoca. ¿Por qué no pérdida de memoria? – BSalunke

Respuesta

32

El problema no es si sus clases asignan memoria dinámicamente. Es si un usuario de las clases asigna un objeto B a través de un Un puntero y luego lo elimina:

A * a = new B; 
delete a; 

En este caso, si no hay un destructor virtual para A, el estándar de C++ dice que su programa exhibe indefinido comportamiento. Esto no es bueno.

Este comportamiento se especifica en la sección 5.3.5/3 de la Norma (en este caso refiriéndose a delete):

si el tipo estático del operando es diferente de su tipo dinámico, el tipo estático debe ser una clase base de el tipo dinámico del operando y el tipo estático tendrá un destructor virtual o el comportamiento es indefinido.

+3

Aunque la respuesta es correcta, siempre vas al estándar -sin dar ninguna explicación de descenso- las respuestas deberían enseñar a los nuevos programadores a no predicar (o validar el conocimiento de) los experimentados. –

+0

El estándar no indica por qué es un comportamiento indefinido, como tampoco lo hace para dividir por cero. –

+3

@Autopopulated La gente tiene algunas ideas muy extrañas acerca de esta. cualquiera pensaría que agregar un destructor virtual agregaba una gran sobrecarga. dadas las racionalizaciones que producirán para evitar hacerlo. –

4

La memoria de liberación no es la única función crítica que puede realizar un destructor. También se puede usar para restablecer el estado global, por ejemplo. No hacer esto no perderá memoria, pero podría causar otros problemas en su programa.

Además, incluso si su destructor no hace nada útil hoy en día, puede que en algún momento en el futuro. No hay una razón real para evitar un destructor virtual si tiene herencia, entonces ¿por qué no agregarlo y dormir mejor por la noche?

+0

No sé por qué esto fue desestimado ... –

+1

(+1) Supongo que alguien está de nuevo en su habitual turno de votación descendente. La respuesta agrega valor. –

+1

@Autopulated sí Neil tiene razón. Pero puede haber más de una respuesta correcta. El mío ciertamente no es incorrecto. – JaredPar

0

El destructor de la clase padre siempre se llama automáticamente, y el dtor predeterminado siempre se genera si no se declara explícitamente dtor. En su ejemplo, ni A ni B necesitan tener un dtor no trivial.

Si su clase tiene funciones virtuales, un dtor virtual adicional no duele, y es una buena práctica. En caso de que su clase asigne memoria o cualquier otro recurso (como abrir un archivo), se necesita un dtor para liberar ese recurso nuevamente después de la destrucción.

+0

No obtuvo el propósito de un destructor virtual. No tiene nada que ver con lo que hace tu clase base y todo tiene que ver con cómo otros (sobre los cuales no tienes control) usan tu clase. –

+0

Aha. Tal vez no explique lo suficiente ... y no he separado la discusión del dtor virtual de la presencia general de un dtor no trivial. Sé que no tengo control sobre lo que otros hacen con mi clase, pero puedo expresar algunas intenciones. Un destructor virtual (u otras funciones virtuales) le dice que se espera que se deriva de esa clase. Si falta un dtor virtual, podría ser porque no se pretende derivar de esa clase, al menos no de manera tal que las clases derivadas se destruyan usando un puntero a la clase base. – tobing

19

El propósito de virtual destructor (es decir, el propósito de hacer un destructor virtual) es facilitar la eliminación polimórfica de objetos a través de delete-expresión. Si su diseño no requiere la eliminación polimórfica de objetos, no necesita destructores virtuales. En referencia a su ejemplo, si alguna vez tiene que eliminar un objeto del tipo B mediante un puntero de tipo A * (eliminación polimórfica), necesitará un destructor virtual tan alto en la jerarquía como A. Así es como se ve desde un punto de vista formal.

(Nota, por cierto, como dijo Neil, que lo importante es la forma de crear/borrar sus objetos de clase, no cómo las clases gestionar su memoria interna.)

En cuanto a las buenas prácticas de programación ... depende en su intento y su diseño al final.Si sus clases no están diseñadas para ser polimórficas en absoluto (sin métodos virtuales), entonces no necesita destructores virtuales. Si su clase es polimórfica (tiene al menos un método virtual), entonces hacer que el destructor sea virtual "por las dudas" podría ser una muy buena idea, y en este caso lleva consigo prácticamente ninguna penalización de rendimiento/memoria.

Esto último generalmente se expresa como una guía de buenas prácticas bastante conocida: si su clase tiene al menos un método virtual, también haga que el destructor sea virtual. Aunque desde el punto de vista formal, un destructor virtual podría no ser realmente necesario allí, sigue siendo una buena guía para seguir.

Las clases que no tienen recursos pero pueden formar jerarquías polimórficas siempre deben definir destructores virtuales vacíos, excepto que es perfectamente suficiente para definir un destructor virtual explícito vacío (e incluso puro) en la base misma de la jerarquía. Todos los demás destructores se volverán virtuales automáticamente, incluso si el compilador los define implícitamente. Es decir. no tiene que definir explícitamente un destructor vacío en cada clase. Solo la base es suficiente.

+0

AndreyT: ¿para que las clases que no tienen recursos pero pueden formar jerarquías polimórficas siempre definan destructores virtuales vacíos? –

+3

@Eli Bendersky: Sí, exactamente. Excepto que, por supuesto, es perfectamente suficiente definir un destructor virtual explícito vacío (e incluso puro) en la base misma de la jerarquía. Todos los demás destructores se volverán virtuales automáticamente, incluso si el compilador los define implícitamente. Es decir. no es necesario * explícitamente * definir un destructor vacío en * cada * clase. Solo la base es suficiente. – AnT

+0

Excelente trabajo para explicar los detalles. – JadziaMD

0

El propósito de declarar destructor como virtual es poder invocar el destructor de la clase derivada cada vez que se invoca delete en un puntero de tipo Base que apunta al objeto de tipo Derivado. No hacerlo daría como resultado un comportamiento indefinido.

La suposición de que no necesita marcar el destructor como virtual si no está asignando memoria dinámicamente implica que no necesita llamar al destructor de clase derivado si no está asignando memoria dinámicamente, lo cual es incorrecto. Como todavía puede hacer otras operaciones en el destructor de su clase derivada además de simplemente desasignar la memoria asignada dinámicamente. Ejemplos serían cerrar un archivo abierto, registrar alguna información, etc.

+0

De hecho, es más estricta que eso. Por supuesto, todos estamos de acuerdo en que en este caso solo se llama 'A :: ~ A()' en lugar de 'B :: ~ B()' si el 'operador de eliminación' de alguna manera usa la información de tamaño del tipo para saber cuánto debería ser liberado, ¿qué pasará? 'delete' es la implementación definida, y este comportamiento indefinido, por lo que nadie lo sabe, aparte de la ingeniería inversa (o lecturas de las especificaciones) de una implementación determinada. –

+0

Gracias ... no conozco este aspecto – mukeshkumar

-1

Si su única preocupación es la memoria, quizás debería comenzar por proteger el destructor de la clase base (y/o tal vez otros). Entonces, si algo no compila, verás por qué. Ref: boost :: cualquier forma.

+0

¿Podría explicar su referencia? –

Cuestiones relacionadas