Estilo C++: Prefijando la palabra clave virtual a métodos anulados


He estado teniendo una discusión con mis compañeros de trabajo sobre si prefijar métodos reemplazados con la palabra clave virtual, o solo en la clase base originaria.

Tiendo a anteponer todos los métodos virtuales (es decir, los métodos que implican una búsqueda de vtable) con la palabra clave virtual. Mi razonamiento es triple:

  1. Dado que C++ carece de un override palabra clave, la presencia de lo virtual palabra clave al menos le notifica que el método implica una búsqueda y teóricamente podría ser anulado por otras especializaciones, o podrían ser a través de un puntero a una mayor clase base.

  2. Uso consistente de este estilo significa que, cuando ves un método (al menos dentro de nuestro código) sin la palabra clave virtual, usted puede inicialmente asumir que no es ni derivado de una base ni especializada en subclase.

  3. Si, por algún error, el virtual fueron eliminados de IFoo, todos los niños todavía funcionarán (Cfooespecialización:: DoBar haría todavía anular CFooBase:: DoBar, en lugar de simplemente ocultarlo).

El argumento en contra de la práctica, tal como la entendí, era, "Pero ese método no es virtual" (que creo que no es válido, y nace de un malentendido de virtualidad), y "Cuando veo la palabra clave virtual, espero que eso signifique que alguien está derivando de ella, y vaya a buscarla."

Las clases hipotéticas pueden estar distribuidas en varios archivos, y hay varias especializaciones.

class IFoo {
public:
    virtual void DoBar() = 0;
    void DoBaz();
};

class CFooBase : public IFoo {
public:
    virtual void DoBar(); // Default implementation
    void DoZap();
};


class CFooSpecialization : public CFooBase {
public:
    virtual void DoBar(); // Specialized implementation
};

Estilísticamente, ¿eliminarías la palabra clave virtual de las dos clases derivadas? Si es así, ¿por qué? ¿Cuáles son los pensamientos de Stack Overflow aquí?

Author: Kara, 2009-09-03

6 answers

Estoy completamente de acuerdo con su razonamiento. Es un buen recordatorio de que el método tendrá semántica de despacho dinámico cuando se llame. El argumento "ese método no es virtual" que tu compañero de trabajo está usando es completamente falso. Ha mezclado los conceptos de virtual y puro-virtual.

 23
Author: Tyler McHenry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-03 01:12:56

Una función una vez virtual siempre virtual.

Entonces, en cualquier caso, si la palabra clave virtual no se usa en las clases posteriores, no impide que la función/método sea 'virtual', es decir, se anule. Así que uno de los proyectos en los que trabajé, tenía la siguiente guía que me gustó un poco:

  • Si se supone que la función/método ser anulado siempre use el palabra clave 'virtual'. Esto es especialmente true cuando se usa en la interfaz / basar clase.
  • Si se supone que la clase derivada ser subclasificado más claramente indique la palabra clave 'virtual' para cada función/método que puede ser anular. C++11 usa la palabra clave 'override'
  • Si la función / método en el derivado la clase no se supone que sea subclasificada de nuevo, luego la palabra clave 'virtual' debe ser comentado indicando que la función / método fue anulado, pero no hay otras clases que lo anulan nuevo. Esto, por supuesto, no impide alguien de anular en el clase derivada a menos que la clase se hace final (no derivable), pero indica que el método no debe ser anular. Ex: /*virtual*/ void guiFocusEvent(); C++11, usa la palabra clave 'final' junto con la 'override' Ex: void guiFocusEvent() override final;
 6
Author: Abhay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-04 16:50:52

Agregar virtual no tiene un impacto significativo de ninguna manera. Tiendo a preferirlo, pero es realmente un tema subjetivo. Sin embargo, si usted se asegura de utilizar el override y sealed palabras clave en Visual C++, obtendrás una mejora significativa en la capacidad de detectar errores en tiempo de compilación.

Incluyo las siguientes líneas en mi PCH:

#if _MSC_VER >= 1400
#define OVERRIDE override
#define SEALED sealed
#else
#define OVERRIDE
#define SEALED
#endif
 4
Author: Sam Harwell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-03 01:47:37

Tendería a no usar ninguna sintaxis que el compilador me permita omitir. Dicho esto, parte del diseño de C# (en un intento de mejorar sobre C++) era requerir que las sobreescrituras de los métodos virtuales se etiquetaran como "sobreescritura", y eso parece ser una idea razonable. Mi preocupación es que, ya que es completamente opcional, es solo cuestión de tiempo antes de que alguien lo omita, y para entonces habrás adquirido el hábito de esperar que las anulaciones tengan "virtual" especificado. Tal vez es mejor simplemente vivir dentro de las limitaciones del idioma, entonces.

 3
Author: Steven Sudit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-03 01:38:35

Puedo pensar en una desventaja: Cuando una función miembro de clase no se sobrescribe y se declara virtual, se agrega una entrada uneccessary en la tabla virtual para esa definición de clase.

 0
Author: johnB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-30 16:20:33

Nota: Mi respuesta se refiere a C++03 con el que algunos de nosotros todavía estamos atrapados. C++11 tiene las palabras clave override y final como @JustinTime sugiere en los comentarios que probablemente deberían usarse en lugar de la siguiente sugerencia.

Ya hay muchas respuestas y dos opiniones contrarias que más destacan. Quiero combinar lo que @280Z28 mencionó en su respuesta con la opinión de @StevenSudit y las pautas de estilo de @Abhay.

No estoy de acuerdo con @280Z28 y no usaría Extensiones de idioma de Microsoft a menos que esté seguro de que solo usará ese código en Windows.

Pero me gustan las palabras clave. Entonces, ¿por qué no usar una adición de palabra clave # define-d para mayor claridad?

#define OVERRIDE
#define SEALED

O

#define OVERRIDE virtual
#define SEALED virtual

La diferencia es tu decisión sobre lo que quieres que suceda en el caso que describes en tu 3er punto.

3-Si, por algún error, los virtuales fueron eliminados de IFoo, todos los niños seguirán funcionando (CFooSpecialization:: DoBar todavía anularía CFooBase:: DoBar, en lugar de simplemente ocultarlo).

Aunque yo diría que es un error de programación por lo que no hay "arreglo" y probablemente ni siquiera debería molestarse en mitigarlo, sino que debe asegurarse de que se bloquea o notifica al programador de alguna otra manera (aunque no puedo pensar en uno en este momento).

Si eliges la primera opción y no te gusta agregar #define, entonces puedes usar comentarios como:

/* override */
/* sealed */

Y eso debería hacer el trabajo para todos los casos donde quieres claridad , porque no considero que la palabra virtual sea lo suficientemente clara para lo que quieres que haga.

 0
Author: nonsensickle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-19 00:57:48