2011-06-30 8 views
8
class A 
{ 
    public: 
    virtual ~A() 
    { 
    } 
}; 

class B : virtual public A 
{ 
    public: 
    ~B() throw() 
    {} 
}; 

class C : public B 
{ 
}; 

int main(int argc, char * argv []) 
{ 
return 0; 
} 

Ese código da el siguiente error:especificación de excepción de gcc destructor predeterminado

error: looser throw specifier for ‘virtual C::~C()’ 
error: overriding ‘virtual B::~B() throw()’ 

en mi Debian Testing (gcc (Debian 4.6.0-10) 4.6.1 20110526 (preliminar)), pero compila sin errores en versiones anteriores de gcc (4.5 en mi sistema Debian nuevamente).

How does an exception specification affect virtual destructor overriding? De acuerdo con esa respuesta, el compilador debe crear un constructor predeterminado que coincida con la declaración throw de la clase base. Obviamente, esto no es lo que sucede en el nuevo gcc. ¿Qué ha cambiado? ¿Cuál es el comportamiento correcto del compilador? ¿Existe alguna solución fácil al problema que no sea la adición manual de destructores vacíos en las clases derivadas (indicador del compilador, por ejemplo)?

+0

¿Es ese el código real? Si omitió una declaración de miembro, eso puede cambiar el resultado. Además, ¿especificó compatibilidad con C++ 11? Las reglas han cambiado un poco destructores wrt y, aunque el código aún debería estar bien, podría haber un error en algún lugar allí. –

+4

No, ese no es el código real. Puedo decirlo porque tiene al menos un error de sintaxis. @Yordan, publique un código real y compilable cuando haga una pregunta. Para obtener detalles sobre cómo hacerlo y por qué es importante, visite http://sscce.org. –

+0

@Rob - O.o Tienes que estar bromeando ..?No es una pregunta con un código fuente complicado y largo, donde tales cosas son importantes. Faltan dos puntos y coma obvios, esto no cambia la pregunta, la información, el entorno, nada. –

Respuesta

3

supongo que en el código real o bien ~A() o ~B() se declara virtual? (El mensaje de error se queja de un destructor virtual, pero en el código escrito ninguno de los destructores es virtual.)

Creo que la herencia virtual es lo que está desencadenando su problema. El destructor de C (definido implícitamente) se requiere para llamar primero al ~B() y luego, porque C es la clase más derivada, para llamar al ~A(). (12.4/6)

La especificación de excepción generada para ~C() es necesaria para permitir la propagación de cualquier excepción, ya que llama directamente al ~A() que no tiene especificación de excepción. (15.4/13)

Y eso desencadena su error: no puede anular una función virtual con una especificación throw() (destructor de B) con una versión que pueda arrojar. (15.4/3)

La solución sería poner throw() en el destructor de A. (Si no puede hacer eso, ¿por qué lo hace en B?)

El error tampoco ocurriría sin la herencia virtual, porque entonces el destructor de C solo llamaría al destructor de B. (El destructor de B todavía llamaría a A, y todavía estás patinando sobre hielo delgado, porque si el destructor de A arroja irás directamente al terminate()).

+0

¡Gracias por la respuesta! sobre A que tiene un desctructor virtual que me he perdido (he corregido esto en el ejemplo ahora). Parece que de las soluciones que ofrece, agregando throw() a A es la correcta para mi caso. –

+0

Una mejor solución es no usa un 'throw' especificación en absoluto. Esta especificación ahora es ampliamente vista como un error en el lenguaje. –

0

GCC 4.X es más estricto que las versiones anteriores, y por lo tanto, puede no indicarlo implícitamente. Intenta decirlo explícitamente.

A mi modo de entender, si la clase B tenía un destructor, que arrojó explícitamente nada (es decir.

class B: public A 
{ 
public: 
virtual ~B() throw { } 
} 

Debe estar bien.

En cualquier caso, la última vez que lo comprobé que era muy mala práctica a lanzar excepciones de d'res.

la esperanza de que ya alguna ayuda!

+0

También fue una mala práctica declarar las especificaciones de excepción –

+0

"hay alguna solución fácil al problema ** además de agregar manualmente destructores vacíos en clases derivadas **". También agregar 'throw()' (note - es el especificador de tiro _empty_ (algo así como 'nothrow' en C++ 0x)) en destructores es diferente a solo usar especificadores de tiro. No es una mala práctica. Incluso se recomienda: los destructores no deberían tirar, ¿verdad? (: –

+0

No estoy seguro de si es posible o no ... pero, por lo que sé, las cosas no deberían descartarse, lo que significa que debe envolver cada vez que elimine un objeto en un intento/catch. Yo diría: no, pero esto parece abusar del mecanismo de lanzar para mí, así que no puedo decir con certeza qué sucede cuando lo abusas de una manera opuesta a la otra. Aunque no, después de leer el extracto de la OP publicar desde la especificación de C++, no hay forma de hacerlo sin especificar los mismos tipos de excepción que la función sobrescrita de la clase base. Supongo que esto tiene que ver con la ambigüedad del tipo de puntero. – Syndacate

Cuestiones relacionadas