2008-11-03 11 views
9

que tienen una clase base con una función virtual opcional¿Las funciones virtuales no puras con los parámetros son una mala práctica?

class Base { 
    virtual void OnlyImplementThisSometimes(int x) {} 
}; 

Cuando compilo este recibo una advertencia acerca del parámetro x no utilizada. ¿Hay alguna otra forma en que debería haber implementado la función virtual? He reescrito así:

class Base { 
    virtual void OnlyImplementThisSometimes(int x) 
    { 
     x = 0; 
    } 
}; 

También tengo el problema de que si no tengo cuidado, la subclase hago puede implementar la función mal y luego no me fijo debido a la sobrecarga: por ejemplo,

class Derived : public Base { 
    void OnlyImplementThisSometimes(int x, int y) { // some code } 
}; 

Derived d; 
Base *b = dynamic_cast<Base *>(&d); 
b->OnlyImplementThisSometimes(x); // calls the method in the base class 

El método de la clase base fue llamado porque he implementado la función derivada con un parámetro "int y" pero no hay ninguna advertencia acerca de esto. ¿Son estos errores comunes en C++ o he entendido mal las funciones virtuales?

Respuesta

22

Haciendo caso omiso de los problemas de diseño, usted puede obtener alrededor de la advertencia del compilador sobre una variable sin usar omitiendo el nombre de la variable, por ejemplo:

virtual void OnlyImplementThisSometimes(int) { } 

implementar erróneamente el método de firma incorrecta cuando se trata de reemplazar la función virtual se encuentra algo que debe tener cuidado en C++. Los lenguajes como C# solucionan esto con la palabra clave 'anular'.

+1

La otra forma de hacer esto (el mismo efecto) es comentar el nombre de la variable en línea, por ejemplo: int/* x * /. Si el nombre de la variable es descriptivo (que debería ser), esto ayudará a los lectores. – Nick

+0

@Nick: Creo firmemente en la seguridad de tipo fuerte, donde el tipo describe la intención (y evita la confusión de argumentos). Si (principalmente) codifica así, los nombres de los parámetros no son importantes. –

+0

El último párrafo se puede editar para reflejar la palabra clave 'override' en C++. – Rotem

6

¿Por qué definirlo en la clase base? Si la clase base no va a usar el método, simplemente defínalo como un método virtual en su clase derivada.

O la implementación predeterminada podría lanzar una excepción

4

Si proporciona una implementación por defecto de una función virtual, debe haber una aplicación correcta para todas las clases derivadas que no anulan esa función. Si no puede proporcionar una implementación correcta, entonces mi consejo sería hacer una función virtual pura y dejarla a la clase derivada para proporcionar una implementación. Las clases derivadas que no permiten que se llame al método pueden lanzar una excepción para garantizar que no se use por error.

+0

Y si puede pensar en una implementación que * normalmente * es correcta, pero no siempre, entonces puede proporcionar una implementación de una función virtual pura, y en el caso normal la clase derivada simplemente la llama. –

2

Esto es algo común en mi código. Por ejemplo, tengo clases que están diseñadas para operación de subproceso único y multiproceso. Hay muchas rutinas y datos comunes. Puse todo eso en la clase base (que también tiene un par de virtuales puros).

Implemento dos funciones virtuales vacías en la clase base: Init() y Limpieza(). La clase derivada de un solo subproceso no los implementa, pero sí el subproceso múltiple.

Tengo una función de fábrica crear la clase derivada apropiada y luego devolver un puntero. El código del cliente solo conoce el tipo de clase base y llama a Init() y Cleanup(). Ambos escenarios hacen lo correcto.

Por supuesto, puede haber otras buenas sugerencias sobre cómo hacer esto, pero este modismo funciona bien para una gran parte de mi código.

+0

Creo que anulando una función virtual pura en la clase derivada y haciendo que simplemente no haga nada da como resultado un código más claro y fácil de usar que haciendo esto en la clase base y no anulando en la clase derivada. Es menos probable que se use incorrectamente, porque el usuario de la clase tiene que pensar en ello. – foraidt

3

Además de simplemente omitiendo el nombre de la variable, en muchos compiladores se puede decir que el compilador, que son conscientes de que no se utiliza y SHUTUP al hacer esto

int func(int x) 
{ 
    (void) x; 
} 
+0

Estrictamente hablando, eso no le dice al compilador que no está siendo utilizado y que está CERRADO. Lo usa. –

2

No es una mala práctica y es una lenguaje común para especificar partes de una clase que son opcionales para implementar.

Actualmente lo estoy usando para un sistema de entrada de usuario, porque sería tedioso para un usuario de esa clase implementar cada método, incluso si es probable que no lo use de todos modos.

class mouse_listener{ 
public: 
    virtual ~mouse_listener() {} 

    virtual void button_down(mouse_button a_Button) {} 
    virtual void button_up(mouse_button a_Button) {} 
    virtual void scroll_wheel(mouse_scroll a_Scroll) {} 
    virtual void mouse_move_abs(math::point a_Position) {} 
    virtual void mouse_move_rel(math::point a_Position) {} 
}; 
2

Por cierto, si usted sabe que su clase base, nunca hay ninguna necesidad de hacer dinámicas hasta -casts, es decir, a partir deriva a la base.

Base *b = &d; 

hará igual de bien, dynamic_cast<> lugar se debe utilizar cuando se establecen a cielo, es decir, desde la base hasta derivó:

if((Derived *d = dynamic_cast<Derived *>(b)) != 0) 
{ 
    // use d 
} 

(Y, por supuesto, en el caso de abajo-yeso, static_cast<> por lo general funciona tan bien)

+0

Puedo pensar al menos en un caso en el que se necesitan conversiones dinámicas: si deriva de un parámetro de plantilla y necesita llamar a funciones específicas basadas en el tipo de base.En mi caso, era algo como esto: if (fixture * f = dynamic_cast (this)) {f-> set_up();} –

+0

Debido a la forma en que está configurado el framework, no sé en ese momento en hora si el caso de prueba se deriva de accesorio o no: el caso de prueba se deriva de un argumento de plantilla. –

11

se define una macro _unused como:.

#define _unused(x) ((void)x) 

definir la función que:

virtual void OnlyImplementThisSometimes(int x) { _unused(x);} 

Esto no sólo mantiene el compilador de quejarse, pero hace que sea obvio para cualquiera mantener el código que no se han olvidado de x - que son intencionalmente ignorarlo .

+0

Si está programando con Qt, puede usar la macro Q_UNUSED que es idéntica a esta solución propuesta. –

-2

La respuesta más simple a esto se muestra a continuación:

class Base { 
    virtual void OnlyImplementThisSometimes(int x) { x;} 
}; 

Una simple referencia a la variable que no hace absolutamente nada va a eliminar todas las advertencias (de VC++ en el nivel más alto de todos modos).

+0

En primer lugar, esto todavía advierte en GCC con -Wall ("la declaración no tiene ningún efecto"). En segundo lugar, no es la respuesta más simple: omitir el nombre del parámetro es. –

-1

Prueba esto:

class Base { 
    virtual void OnlyImplementThisSometimes(int x) = 0; 
}; 

Ha sido un tiempo desde que he hecho cosas por el estilo, pero creo que es así como se declara una función virtual.

También como han dicho otros, los nombres de variables son opcionales en declaraciones de funciones como esta.

Cuestiones relacionadas