2012-01-06 24 views
29
#include <iostream> 
using namespace std; 

class CPolygon { 
    protected: 
    int width, height; 
    public: 
    virtual int area() 
     { return (0); } 
    }; 

class CRectangle: public CPolygon { 
    public: 
    int area() { return (width * height); } 
    }; 

Tiene la advertencia de compilación¿Qué significa la advertencia 'tiene el método virtual ... pero el destructor no virtual' durante la compilación de C++?

Class '[[email protected]' has virtual method 'area' but non-virtual destructor 

¿Cómo entender esta advertencia y cómo mejorar el código?

[EDIT] ¿Esta versión es correcta ahora? (Tratando de dar respuesta a dilucidar a mí mismo con el concepto)

#include <iostream> 
using namespace std; 

class CPolygon { 
    protected: 
    int width, height; 
    public: 
    virtual ~CPolygon(){}; 
    virtual int area() 
     { return (0); } 
    }; 

class CRectangle: public CPolygon { 
    public: 
    int area() { return (width * height); } 
    ~CRectangle(){} 
    }; 
+0

Sí, la nueva versión es correcta. Aunque se considera buena forma para volver a declarar funciones en clases derivadas como virtuales, aunque no es necesario. Esto es para que las personas que solo quieren ver la clase derivada aún sepan que las funciones son virtuales. – Omnifarious

+0

Quiere decir 'clase CRectangle: public CPolygon { public: virtual int area() {return (ancho * alto); } }; '? – qazwsx

+0

Sí. Y 'virtual ~ CRectangle() {}' también. Como dije, repetir que estas funciones son virtuales es simplemente una buena forma, no es requerido por el lenguaje de ninguna manera. – Omnifarious

Respuesta

64

Si una clase tiene un método virtual, eso significa que quiere que otras clases hereden de ella. Estas clases podrían destruirse a través de una referencia de clase base o puntero, pero esto solo funcionaría si la clase base tiene un destructor virtual. Si tienes una clase que supuestamente se puede usar polimórficamente, también debería poder eliminarse polimórficamente.

Esta pregunta también se responde en profundidad here. El siguiente es un completo programa de ejemplo que demuestra el efecto:

#include <iostream> 

class FooBase { 
public: 
    ~FooBase() { std::cout << "Destructor of FooBase" << std::endl; } 
}; 

class Foo : public FooBase { 
public: 
    ~Foo() { std::cout << "Destructor of Foo" << std::endl; } 
}; 

class BarBase { 
public: 
    virtual ~BarBase() { std::cout << "Destructor of BarBase" << std::endl; } 
}; 

class Bar : public BarBase { 
public: 
    ~Bar() { std::cout << "Destructor of Bar" << std::endl; } 
}; 

int main() { 
    FooBase * foo = new Foo; 
    delete foo; // deletes only FooBase-part of Foo-object; 

    BarBase * bar = new Bar; 
    delete bar; // deletes complete object 
} 

Salida:

Destructor of FooBase 
Destructor of Bar 
Destructor of BarBase 

Tenga en cuenta que ambas causas delete bar; destructores, ~Bar y ~BarBase, que se llamará, mientras que sólo las llamadas delete foo;~FooBase. El último es incluso undefined behavior, por lo que el efecto no está garantizado.

+0

Esta respuesta se mejoraría mucho con un ejemplo que demuestra los efectos nocivos del corte. Obtendrá un voto de mi parte cuando lo tenga. – Omnifarious

+1

@Omnifarious: agregué un ejemplo. –

+0

Para que quede claro: 'delete foo' invoca un comportamiento indefinido, no se garantiza que solo ejecute' ~ FooBase'. – Mankarse

12

que significa que necesita un destructor virtual en una clase base con métodos virtuales.

struct Foo { 
    virtual ~Foo() {} 
    virtual void bar() = 0; 
}; 

de no utilizarla es puede conducir a un comportamiento indefinido, por lo general aparece como una pérdida de memoria en herramientas como valgrind.

+0

Es solo UB si elimina un objeto de subclase mediante una referencia o puntero de clase base. –

+3

Dejarlo no es, en sí mismo, un comportamiento indefinido. El único comportamiento indefinido que podría causar es si un objeto dinámicamente asignado de un tipo derivado se desasigna con una expresión 'delete' donde el tipo del operando es de puntero a la clase base donde la clase base no tiene un destructor virtual. Hay otras opciones para alentar el uso seguro, como dar a la clase un destructor no virtual protegido. –

+0

Por lo que he escuchado, principalmente lleva a problemas si usted (o alguien) decide extender esa estructura. –

1

Simplemente significa que un código como

CPolygon* p = new CRectangle; 
delete p; 

... o lo que sea lo que sea envoltura en puntero inteligente, será esencialmente se comporta correctamente, ya CPolygon no es polimórfico en la eliminación y la parte CRectange no será destruido apropiadamente

Si no va a eliminar CRectangle y CPolygon polimórficamente, esa advertencia no es significativa.

+2

Pero si no va a eliminar CRectangle y CPolygon polimórficamente, el destructor de la clase base debe estar protegido para aplicar esto en tiempo de compilación. –

+0

@MarkB: No necesariamente: si CPolygon no es abstracto (no sé la abstracción de OP qué tan profunda es), tanto CRect como CPolygon pueden ser verdaderos ciudadanos legales perfectos de la pila, participando en los algoritmos de CPolygon a través de referencias. El área necesita ser calculada polimórficamente, pero no se necesita destrucción polimórfica. Y se requiere que CPolygon sea en sí mismo destructible (no destructor protegido). Derivar una clase que no tiene un destructor virtual no es diferente de derivar una clase que no tiene TODOS los métodos virtuales. Simplemente no espere que lo que no es virtual se comporte virtualmente. –

+0

@EmilioGaravaglia: Normalmente se recomienda evitar derivar de una clase concreta. Está demasiado abierto para rebanar involuntariamente en una variedad de formas diferentes. –

Cuestiones relacionadas