2010-09-21 27 views
5

vi el siguiente fragmento de código:C++ Excepción tirar anotaciones sobre las funciones virtuales

class Foo 
{ 
public: 
     void virtual func() throw (int, float) = 0; 
}; 

class Bar : public Foo 
{ 
public: 
     void virtual func() throw(short);  // line 1: compile error " 
                     // looser throw specifier" 
     void virtual func() throw();    // line 2: can compile 
     void virtual func() throw(float, int); // line 3: can compile 
     void virtual func() throw(float);  // line 4: can compile 
     void virtual func() throw(int);   // line 5: can compile 

}; 

int main(void) 
{ 
     return 1; 
} 

Q1> ¿Cuál es el significado de

void virtual func() throw (int, float) = 0; 

Q2> qué línea 1 no pueden pasar el compilador?

Gracias

Respuesta

8

Analicemos esto.La declaración:

void virtual func() throw (int, float) = 0; 

tiene 2 construcciones sobre las que pregunta. el constructo =0 le dice al compilador que la función declarada es 'abstracta', que le dice al compilador que la función no necesita ser definida en el class Foo (aunque puede ser - pero generalmente no lo es) y que un objeto class Foo no puede ser creado directamente, ya sea local, global o vía new. Sin embargo, puede tener punteros o referencias a objetos de class Foo. Algunas clases derivadas necesitan anular la función como una función no abstracta: los objetos de esa clase se pueden crear directamente (siempre que no haya otras funciones abstractas que no se hayan hecho "concretas").

La construcción throw (int, float) es una especificación de excepción. Esto le dice al compilador que el contrato de la función es que solo lanzará excepciones de tipo int o float si arroja una excepción. Si la función arroja algún otro tipo de excepción, el compilador está obligado a manejar eso especialmente (llamando al std::unexpected()).

Ahora, si intenta reemplazar esa función en una clase derivada con la siguiente declaración:

void virtual func() throw(short); 

¿Estás diciendo que el contrato de la función es que va a lanzar excepciones de tipo short si una excepción es arrojado. Sin embargo, lanzar short no es parte del contrato de la función que se reemplaza, por lo que el compilador no lo permite.

Si se declara la anulación de esta manera:

void virtual func() throw(float); 

¿Estás diciendo que la anulación puede lanzar una float, que forma parte del contrato de la declaración original (si nunca lanza un int que doesn No rompa el contrato: el contrato original solo dice que la función puede arrojar un int, aunque no es necesario.

La parte relevante de la norma es de 15.4/3 especificaciones Excepción:

Si una función virtual tiene un excepción de especificación, todos declaraciones, incluida la definición , de cualquier función que anulaciones que la función virtual en cualquier clase derivada de solo permitirá las excepciones permitidas por la especificación de excepción de la función virtual de clase base .

Tenga en cuenta que la norma establece explícitamente que una especificación de excepción no es parte de tipo de la función (15,4/12), así que un puntero de función puede, por ejemplo, punto a las funciones que tienen diferentes especificaciones de excepción.

+0

Hola Michael, gracias por tu excelente respuesta. – q0987

6

que está definiendo la misma función de firma muchas veces. Los diferentes calificadores throw() no son suficientes para eliminar la ambigüedad de las funciones.

El calificador throw() simplemente significa que la función especificada solo arrojará los tipos enumerados entre paréntesis después del calificador. Sin embargo, esto no impide que la función se lance. Por el contrario, si la función realmente hace lanzar cualquier tipo no listado, su programa terminará.

+1

Eso está al revés. Si la función arroja cualquiera de los tipos listados, la excepción puede ser capturada y manejada. Son tipos NO enumerados que harán que el programa finalice (a través de 'std :: unexpected'). –

+0

Derecha - se arregló. –

2

El significado de las declaraciones throw designadas es declarar que una función solo puede lanzar esas excepciones nombradas directa o indirectamente.

Así la línea:

void virtual func() throw(int, float) =0; 

significa que cualquiera que sea la clase hereda este tipo de base que sólo se le permite lanzar ya sea un entero o un flotador. No puede arrojar, directa o indirectamente, ningún otro tipo de excepción u objeto. Si lo hace, llamará a la función unexcepted(). Por defecto esto llama a la función terminate(). Puedes restablecer eso usando la función set_unexpected pero aún así.

Al elegir agregar esas declaraciones de lanzamiento a su interfaz, realmente se está limitando.

+2

En realidad, si la función rompe su contrato, se usará 'std :: unexpected' en lugar del valor arrojado. Un comentario no haría eso. –

+0

En realidad, leerlo de la manera en que lo escribí es muy engañoso. Espera un segundo. – wheaties

+0

typo: unexcepted versus unexpected –

3

La función que está definiendo en la clase base es una garantía: solo puede arrojar un int o un float. Su línea 1 está fallando porque está rompiendo la garantía diciendo que arrojará un short, que no es cualquiera de los anteriores.

El = 0 en el Q1 declara que cada clase derivada que intente crear una instancia de tendrá que proporcionar su propia declaración e implementación de esta función. La clase base también puede proporcionar una implementación, pero por lo general no es así.

+0

El modificador puro '= 0' no impide tener una implementación concreta en la clase base. –

+0

@Ben, por supuesto, tienes razón. Repararé mi respuesta. –

2

Cuando anula una función virtual, cualquier especificador de excepciones que proporcione debe ser al menos tan restrictivo como el especificado en la función que está anulando. Esto evita que se viole la especificación de excepción de la clase base.

Como el especificador de excepciones de la clase base [throw (int, float)] no permite que se genere un short, la clase derivada no puede permitir que se genere un short. A lo sumo puede permitir int y/o float; puede permitir que solo uno o ninguno sea lanzado, ya que cualquiera de estas posibilidades sería más restrictiva que la especificación de excepción de la función de clase base.

Cuestiones relacionadas