2009-01-30 24 views
92

Tengo una clase base con una función virtual y quiero anular esa función en una clase derivada. ¿Hay alguna manera de hacer que el compilador compruebe si la función que declare en la clase derivada realmente anula una función en la clase base? Me gustaría agregar alguna macro o algo que asegure que no haya declarado accidentalmente una nueva función, en lugar de anular la anterior.Anular de forma segura las funciones virtuales de C++

Tome este ejemplo:

class parent { 
public: 
    virtual void handle_event(int something) const { 
    // boring default code 
    } 
}; 

class child : public parent { 
public: 
    virtual void handle_event(int something) { 
    // new exciting code 
    } 
}; 

int main() { 
    parent *p = new child(); 
    p->handle_event(1); 
} 

Aquí parent::handle_event() se llama en lugar de child::handle_event(), porque el método del niño no alcanza la declaración const y por lo tanto se declara un nuevo método. Esto también podría ser un error ortográfico en el nombre de la función o una pequeña diferencia en los tipos de parámetros. También puede suceder fácilmente si la interfaz de la clase base cambia y en algún lugar alguna clase derivada no se actualizó para reflejar el cambio.

¿Hay alguna manera de evitar este problema? ¿De alguna manera puedo decirle al compilador o alguna otra herramienta que verifique esto por mí? Cualquier indicador de compilador útil (preferiblemente para g ++)? ¿Cómo se evitan estos problemas?

+2

Gran pregunta, he estado tratando de averiguar por qué mi función de clase niño doesn; t ser llamado desde una hora ahora! –

Respuesta

78

Como g ++ 4.7 se entiende el nuevo C++ 11 override palabra clave:

class child : public parent { 
    public: 
     // force handle_event to override a existing function in parent 
     // error out if the function with the correct signature does not exist 
     void handle_event(int something) override; 
}; 
+0

Muchas gracias por la información. –

+0

@hirschhornsalz: Descubrí que cuando implementa la función handle_event y anexa la anulación al final de la implementación de la función, g ++ da un error; si proporciona una implementación de función en línea en la declaración de clase que sigue a la palabra clave override, todo está bien. ¿Por qué? – h9uest

+3

@ h9uest 'override' se debe usar en la definición. Una implementación en línea es a la vez definición e implementación, por lo que está bien. – hirschhornsalz

3

Sugerir un ligero cambio en su lógica. Puede o no funcionar, dependiendo de lo que necesite lograr.

handle_event() aún se puede hacer el "código predeterminado aburrido" pero en lugar de ser virtual, en el punto donde se quiere que haga el "nuevo código emocionante" la clase base llama a un método abstracto (es decir, debe ser -overridden) método que será provisto por su clase descendiente.

EDIT: Y si más adelante decide que algunas de sus clases descendientes hacen no necesidad de proporcionar "nuevo código emocionante", entonces usted puede cambiar lo abstracto a virtual y proporcionar una implementación de la clase base vacía de esa funcionalidad "insertada" .

2

Su compilador puede tener una advertencia que puede generar si se oculta una función de clase base. Si lo hace, habilítalo. Eso atrapará los choques de const y las diferencias en las listas de parámetros. Lamentablemente, esto no descubrirá un error ortográfico.

Por ejemplo, esto es una advertencia C4263 en Microsoft Visual C++.

20

Algo parecido a la palabra clave override de C# no es parte de C++.

En gcc, -Woverloaded-virtual advierte contra la ocultación de una función virtual de la clase base con una función del mismo nombre pero con una firma suficientemente diferente que no la anule. Sin embargo, no lo protegerá de no anular una función debido a la ortografía incorrecta del nombre de la función en sí.

+2

Es si está usando Visual C++ –

+4

El uso de Visual C++ no convierte 'anular' en una palabra clave en C++; sin embargo, puede significar que está utilizando algo que puede compilar algún código fuente inválido de C++. ;) –

+3

El hecho de que la anulación es inválida C++ significa que el estándar es incorrecto, y no Visual C++ – Jon

15

Por lo que yo sé, ¿no puedes simplemente hacerlo abstracto?

class parent { 
public: 
    virtual void handle_event(int something) const = 0 { 
    // boring default code 
    } 
}; 

Pensé que leí en www.parashift.com que realmente puede implementar un método abstracto. Lo cual tiene sentido para mí personalmente, lo único que hace es forzar subclases para implementarlo, nadie dijo nada acerca de que no se le permita tener una implementación en sí misma.

+0

¡Ahora solo me he dado cuenta de que esto funciona en algo más que destructores! Gran hallazgo – strager

+1

Hay un par de posibles inconvenientes en esto: 1) la otra cosa que marcar uno o más métodos como abstract hace es hacer que la clase base no sea instanciable, lo que puede ser un problema si eso no forma parte del uso previsto de la clase. 2) la clase base podría no ser suya para modificar en primer lugar. –

+3

Estoy de acuerdo con Michael Burr. Hacer el resumen de la clase base no es parte de la pregunta.Es perfectamente razonable tener una clase base con funcionalidad en un método virtual, que desee que anule una clase derivada. Y es igual de razonable querer protegerse contra otro programador que renombra la función en la clase base y hace que su clase derivada ya no la anule. La extensión "anular" de Microsoft es invaluable en este caso. Me encantaría ver que se agregue al estándar, porque desafortunadamente no hay una buena manera de hacerlo sin él. – Brian

11

En MSVC, puede utilizar la palabra clave CLR override incluso si no está compilando para CLR.

En g ++, no hay manera directa de aplicar eso en todos los casos; otras personas han dado buenas respuestas sobre cómo detectar diferencias de firma usando -Woverloaded-virtual.En una versión futura, alguien podría agregar sintaxis como __attribute__ ((override)) o su equivalente usando la sintaxis de C++ 0x.

5

Haga que la función sea abstracta, de modo que las clases derivadas no tengan otra opción que anularla.

@Ray Tu código no es válido.

class parent { 
public: 
    virtual void handle_event(int something) const = 0 { 
    // boring default code 
    } 
}; 

Las funciones de resumen no pueden tener cuerpos definidos en línea. Debe ser modificado para convertirse en

class parent { 
public: 
    virtual void handle_event(int something) const = 0; 
}; 

void parent::handle_event(int something) { /* do w/e you want here. */ } 
9

En MSVC++ puede utilizar keyword override

 
    class child : public parent { 
    public: 
     virtual void handle_event(int something) override { 
     // new exciting code 
     } 
    }; 

override obras tanto para el código nativo y CLR en MSVC++.

Cuestiones relacionadas