2010-06-03 7 views
13

Cuando se declara un método como virtual en una clase, sus reemplazos en las clases derivadas se consideran automáticamente virtual también, y el lenguaje C++ hace que esta palabra clave virtual opcional en este caso:¿Por qué es 'virtual' opcional para los métodos reemplazados en las clases derivadas?

class Base { 
    virtual void f(); 
}; 
class Derived : public Base { 
    void f(); // 'virtual' is optional but implied. 
}; 

Mi pregunta es: ¿Qué ¿El fundamento para hacer virtual es opcional?

Sé que no es absolutamente necesario que se lo diga al compilador, pero creo que los desarrolladores se beneficiarían si el compilador aplicara tal restricción.

P. ej., Cuando leo el código de otros me pregunto si un método es virtual y tengo que rastrear sus superclases para determinarlo. Y algunos estándares de codificación (Google) lo convierten en un 'obligatorio' para poner la palabra clave virtual en todas las subclases.

Respuesta

10

Sí, sería realmente mejor hacer que el compilador aplique el virtual en este caso, y estoy de acuerdo en que este es un error en el diseño que se mantiene por compatibilidad con versiones anteriores.

Sin embargo hay un truco que sería imposible sin ella:

class NonVirtualBase { 
    void func() {}; 
}; 

class VirtualBase { 
    virtual void func() = 0; 
}; 

template<typename VirtualChoice> 
class CompileTimeVirtualityChoice : public VirtualChoice { 
    void func() {} 
}; 

Con lo anterior tenemos que compilar selección de tiempo de carnero queremos virtualidad de func o no:

CompileTimeVirtualityChoice<VirtualBase> -- func is virtual 
CompileTimeVirtualityChoice<NonVirtualBase> -- func is not virtual 

... pero de acuerdo, es un beneficio menor por el costo de buscar la virtualidad de una función, y yo mismo, siempre trato de escribir virtual donde sea aplicable.

+0

Creo que le gustaría eliminar '= 0' de' NonVirtualBase' :) –

+0

@Matthieu - ¡buena captura! Hoy tengo bastante sueño xD –

2

Dado que el lenguaje no puede imponer el estilo "bueno", C++ generalmente ni siquiera lo intenta. Al menos IMO, está abierto a cuestionarse si incluir especificadores redundantes como este es un buen estilo en cualquier caso (personalmente, odio cuando están allí).

(Al menos partes de) Las normas de codificación de Google pueden tienen sentido en algunas circunstancias, pero en lo que se refiere a C++ en general, generalmente se consideran un consejo mediocre en el mejor de los casos. Hasta cierto punto, incluso admiten que algunos de ellos declaran abiertamente que realmente están ahí para encajar con su código anterior. Otras partes no lo admiten tan directamente, y (para ser del todo honesto) que el argumento no respaldaría algunos de sus estándares de todos modos (es decir, algunos de ellos parecen carecer de una justificación real).

+0

No es que el lenguaje "no puede", sino que está en contra de la filosofía C++. Un compilador puede detectar una gran variedad de violaciones de estilo, incluso si se reutilizó "virtual" en la clase derivada. –

+0

@Michael: Tal vez lo mencioné mal, pero mi intención era decir que no se puede imponer un buen estilo en general, por lo que con frecuencia no se intenta, incluso en los casos en que claramente podría (imponer algo que algunas personas consideren un buen estilo) –

3

Debilidad en el diseño, estoy de acuerdo. También creo que habría realmente agradable si había una sintaxis diferente para dos cosas diferentes:

  1. Declarar una función virtual. Es decir. la función que puede ser anulada en la clase derivada. (Esto realmente agrega una nueva entrada de función en el vtable.)
  2. Anulando una función virtual en la clase derivada.

Con las reglas actuales de C++ al anular una función, es fácil atornillar cosas. Si escribe mal el nombre de la función (o comete un error en su lista de parámetros) - entonces realmente hace (1) en lugar de (2).

Y no tiene ningún error/advertencia. Solo consigue la sorpresa en el tiempo de ejecución.

+1

Una de las cosas que Objeto Pascal obtuvo correctamente ... –

+1

Estoy de acuerdo, creo que proviene de Bjarne Stroustrup y el comité desea tener un conjunto de palabras clave lo más pequeño posible ... reutilizando así el mismo para diferentes cosas En la misma línea, realmente no aprecio no tener una advertencia cada vez que ocurre el sombreado: / –

0

Esa es una buena pregunta, y ciertamente estoy de acuerdo en que es un buen estilo redeclarar un método virtual en la clase derivada si se ha declarado virtual en la clase base. Si bien hay algunos idiomas que crean estilo en el idioma (por ejemplo, Google Go y, en cierta medida, Python), C++ no es uno de esos idiomas. Si bien es posible que un compilador detecte que una clase derivada no reutiliza la palabra clave "virtual" para algo declarado "virtual" en una clase base (o, más importante aún, que la clase derivada declara una función del mismo nombre que la clase base y no se ha declarado virtual en la clase base), de hecho, hay configuraciones en muchos compiladores para emitir advertencias (e incluso mensajes de error) en caso de que esto ocurra. En esta etapa, sin embargo, no sería práctico instituir tal requisito en el lenguaje ya que existe demasiado código que no es tan estricto. Además, los desarrolladores siempre pueden elegir ser más estrictos que el lenguaje y pueden subir los niveles de advertencia del compilador.

6

Como nota relacionada, en C++ 0x tiene la opción de aplicar ser explícito con sus modificaciones mediante la nueva sintaxis de atributos.

struct Base { 
    virtual void Virtual(); 
    void NonVirtual(); 
}; 

struct Derived [[base_check]] : Base { 
    //void Virtual(); //Error; didn't specify that you were overriding 
    void Virtual [[override]](); //Not an error 
    //void NonVirtual [[override]](); //Error; not virtual in Base 
    //virtual void SomeRandomFunction [[override]](); //Error, doesn't exist in Base 
}; 

También puede especificar cuando se desea ocultar un miembro a través del atributo [[hiding]]. Hace que su código sea un poco más detallado, pero puede detectar muchos errores molestos en tiempo de compilación, como si lo hiciera void Vritual() en lugar de void Virtual() y terminara introduciendo una función completamente nueva cuando tuviera la intención de anular una existente.

Cuestiones relacionadas