2008-09-24 8 views
57

He definido una interfaz en C++, es decir, una clase que solo contiene funciones virtuales puras.Advertencia del compilador de GNU "la clase tiene funciones virtuales pero destructor no virtual"

quiero prohibir explícitamente a los usuarios de la interfaz para eliminar el objeto a través de un puntero a la interfaz, por lo que declaré un destructor protegido y no virtual para la interfaz, algo así como:

class ITest{ 
public: 
    virtual void doSomething() = 0; 

protected: 
    ~ITest(){} 
}; 

void someFunction(ITest * test){ 
    test->doSomething(); // ok 
    // deleting object is not allowed 
    // delete test; 
} 

El GNU compilador me da una advertencia diciendo:

clase 'ITest' tiene funciones virtuales, pero destructor no virtual

Una vez que el destructor está protegido, ¿cuál es la diferencia al tenerlo virtual o no virtual?

¿Cree que esta advertencia se puede ignorar o silenciar de forma segura?

Respuesta

64

Es más o menos un error en el compilador. Tenga en cuenta que en las versiones más recientes del compilador esta advertencia no se lanza (al menos en 4.3 no). Tener el destructor protegido y no virtual es completamente legítimo en su caso.

Consulte here para obtener un excelente artículo de Herb Sutter sobre el tema. Del artículo:

Directriz n. ° 4: Un destructor de clase base debe ser público y virtual, o estar protegido y no virtual.

+0

Creo que es solo una advertencia, y el caso anterior es excepcional. En general, si tiene funciones virtuales, probablemente también deba tener un destructor virtual. –

+1

Acepto que es un error menor que la advertencia no se suprima en presencia de un destructor protegido. Las otras respuestas que mencionan la eliminación por una clase derivada son válidas, pero es mucho menos probable que sean problemáticas que los destructores virtuales que faltan regularmente. –

+0

¡Gracias por el enlace al artículo de Herb! –

0

Si el destructor es virtual, se asegura de que el destructor de la clase base también se llame antes de realizar la limpieza, de lo contrario, algunas filtraciones pueden ser resultado de ese código. Por lo tanto, debe asegurarse de que el programa no tenga tales advertencias (preferiblemente, no hay advertencias).

9

Algunos de los comentarios sobre esta respuesta se refieren a una respuesta anterior que di, que fue incorrecta.

Un destructor protegido significa que solo se puede invocar desde una clase base, no a través de eliminar. Eso significa que un ITest * no se puede eliminar directamente, solo una clase derivada puede hacerlo. La clase derivada puede querer un destructor virtual. No hay nada de malo en tu código.

Sin embargo, dado que no puede deshabilitar localmente una advertencia en GCC, y que ya tiene un vtable, podría considerar simplemente hacer que el destructor sea virtual. Te costará 4 bytes para el programa (no por instancia de clase), como máximo. Como es posible que hayas dado a tu clase derivada un dtor virtual, es posible que no te cueste nada.

+1

Yo siempre se pregunta por qué el destructor no siempre es virtual ... – INS

+2

2 razones. (1) Usted solo paga por lo que usa en C++. Si el dtor fuera virtual, muchas clases tendrían tablas innecesarias. (2) Ciertos marcos (por ejemplo, CORBA), creo que ahora dependen del comportamiento actual. –

+0

Richard and Airsource, eliminé los comentarios que se referían a la respuesta anterior. Ahora no tengo problemas con su respuesta y cambié mi -1 inicial a +1 ... –

0

Si tenía el código en uno de los métodos de ITest que intentó delete (una mala idea, pero legal), no se invocó el destructor de la clase derivada. Aún debe hacer que su destructor sea virtual, incluso si nunca tiene la intención de eliminar una instancia derivada a través de un puntero de clase base.

+0

No, no debería. El contrato de la interfaz que ha definido es que los usuarios de la interfaz no pueden eliminar instancias de objetos que lo implementan. Es un patrón bastante común para los objetos que se asignan y limpian a través de una fábrica o mediante el recuento de referencias ... (-1) –

4

Si insiste en hacer esto, continúe y pase -Wno-non-virtual-dtor a GCC. Esta advertencia no parece activada de manera predeterminada, por lo que debe haberla habilitado con -Wall o -Weffc++. Sin embargo, creo que es una advertencia útil, porque en la mayoría de las situaciones esto sería un error.

2

Es una clase de interfaz, por lo que es razonable que no deba eliminar los objetos que implementan esa interfaz a través de esa interfaz.Un caso común de eso es una interfaz para objetos creados por una fábrica que debería devolverse a la fábrica. (Tener objetos que contengan un puntero a su fábrica puede ser bastante caro).

Estoy de acuerdo con la observación de que GCC está gimiendo. En su lugar, debería simplemente advertir cuándo elimina un ITest *. Ahí es donde reside el peligro real.

2

Mi opinión personal es que harías lo correcto y el compilador está roto. Deshabilitaría la advertencia (localmente en el archivo que define la interfaz) si es posible,

Me parece que uso este patrón (pequeña 'p') bastante. De hecho, me parece que es más común que mis interfaces tengan direcciones protegidas que para las públicas. Sin embargo, no creo que en realidad sea tan común una expresión idiomática (no se habla mucho de eso) y supongo que cuando la advertencia se agregó a GCC, era apropiado intentar y exigir que la versión anterior fuera virtual. tener la regla de las funciones virtuales. Personalmente, actualicé esa regla para 'dtor debe ser virtual si tienes funciones virtuales y deseas que los usuarios puedan eliminar instancias de la interfaz a través de la interfaz; de lo contrario, el dtor debería estar protegido y no virtual' hace años;)

Cuestiones relacionadas