2010-03-05 22 views
6

Considere el siguiente código:especificación de excepción al redefinir una función virtual


class A 
{ 
public: 
    virtual void f() throw (int) { } 
}; 

class B: public A 
{ 
public: 
    void f() throw (int, double) { } 
}; 

Cuando compilado, se dice que deriva de la clase B tiene un especificador de tiro más flojo en comparación con A. ¿Cuál es la importancia de esto? Si intentamos intercambiar su especificación de excepción, de modo que A :: f() arroje int y double, mientras que B :: f() solo arroja int, el error no aparece.

+2

Sinceramente espero que nunca arroje instancias de clases que no están destinadas a ser usadas como excepciones y esto es solo por una rápida y sucia ilustración de su pregunta :) –

+0

Matthieu: Ah, sí, por supuesto. Entiendo. – jasonline

Respuesta

13
  1. Don't use exception specifications in C++. Es muy contrario a la intuición en comparación con, por ejemplo, los de Java.
  2. Tener una especificación más amplia en los saltos de clase derivados LSP (Principio de sustitución de Liskov).

Para ampliar el punto 2: las personas que llaman A 's esperan que sólo int sale, pero si se utiliza un B (que, porque se deriva públicamente desde A, también significa que es utilizable como un A), de repente double puede salir también, y eso rompería el contrato de A (que solo se arroja int).

+1

Para la opinión de Herb Sutter (que coincide con la de Chris), consulte http://www.gotw.ca/publications/mill22.htm –

+1

De hecho, C++ incluso comprueba esto en declaraciones de funciones implícitas (para funciones a las que llama directamente desde aquellos, su especificación de excepción incluye las especificaciones de la función llamada): 'struct A {virtual ~ A() throw(); }; struct B {~ B() throw (int); }; estructura C: A, B {};/* error: ~ C() tiene throw (int), pero ~ A() tiene throw()! */' –

+0

@Neil: gracias por el enlace; Cambié mi enlace (originalmente uno de GotW) para usarlo. :-) –

1

Su B viola el principio de sustitución de liskov - por ejemplo:

 
void foo(A* a) throw() // ie I will not throw 
{ 
    try 
    { 
    a->f(); 
    } 
    catch(int) 
    {} 
} 

Esto es válido de acuerdo a la interfaz de A; en particular, no esperamos que se arroje un doble. Pero tenga en cuenta si tuviéramos que llamar

foo(new B)

con la interfaz que usted describe, y

B::f()
eran para lanzar un doble.

Cuestiones relacionadas