2011-05-19 22 views
19

Entiendo que los métodos virtuales permiten que una clase derivada anule los métodos heredados de una clase base. ¿Cuándo es apropiado/inapropiado usar métodos virtuales? No siempre se sabe si una clase se subclasificará o no. ¿Todo debería hacerse virtual, solo "por si acaso"? ¿O eso causará una sobrecarga significativa?¿Cuándo es apropiado usar métodos virtuales?

Respuesta

6

Cuando diseñe una clase, debe tener una idea bastante clara de si representa una interfaz (en cuyo caso debe marcar los métodos anulables apropiados y el destructor virtual) O está destinado a ser utilizado tal cual, posiblemente componiendo o compuesto con otros objetos.

En otras palabras, su intención para la clase debe ser su guía. Hacer todo lo virtual a menudo es excesivo y, a veces, engañoso con respecto a qué métodos están destinados a soportar el polimorfismo en tiempo de ejecución.

-1

Eche un vistazo a Design Patterns. Si su código/diseño es uno de estos o similar, use la función virtual. De lo contrario, pruebe this

+2

-1: no existe una relación de causalidad entre el deseo de implementar un patrón de diseño GoF y la necesidad de funciones virtuales. –

3

Si su código sigue un patrón de diseño particular, entonces su elección debe reflejar los principios del DP. Por ejemplo, si está codificando un Decorator pattern, la función que debería ser virtual es la que pertenece a la interfaz del Componente.

De lo contrario, me gustaría seguir un enfoque evolutivo, IOW no tengo métodos virtuales hasta que veo que una jerarquía está tratando de salir de su código.

0

Mi punto es que si quieres usar el puntero de la clase padre para apuntar a la instancia de la clase hija y usar sus métodos, entonces debes usar métodos virtuales.

+0

Si bien es cierto, no creo que esto realmente responda a la pregunta más amplia de "¿cómo sé que necesitaré polimorfismo?" –

2

Una prueba de cordura que uso principalmente es - En caso de que una clase que estoy definiendo se deriva de en el futuro, el comportamiento (función) se mantendría igual o tendría que ser redefinido. Si fuera así, la función es un fuerte contendiente por ser virtual, si no, no, si no lo sé, probablemente necesite examinar el dominio del problema para comprender mejor el comportamiento que planeo implementar. En su mayoría, el dominio del problema me da la respuesta; en los casos en que no lo hace, el comportamiento es generalmente no crítico.

6

Primero una observación ligeramente pedante: en C++ standardese los llamamos funciones miembro, no métodos, aunque los dos términos son equivalentes.

Veo dos razones para NO hacer que una función de miembro sea virtual.

  • "YAGNI" - "No vas a necesitarlo". Si no está seguro de que se derivará una clase, suponga que no será así y no haga que las funciones miembro sean virtuales. Nada dice "no derivar de mí" como un destructor no virtual por cierto. También se trata de intento. Si no es tu intención usar la clase polimórficamente, no hagas nada virtual. Si haces arbitrariamente miembros virtuales, estás invitando a abusos del Liskov Substitution Principle y esas clases de errores son difíciles de rastrear y resolver.
  • Huella de rendimiento/memoria. Una clase que no tiene funciones de miembros virtuales no requiere una tabla virtual (VTable, tabla virtual, utilizada para redirigir llamadas polimórficas a través de un puntero de clase base) y, por lo tanto, (potencialmente) ocupa menos espacio en la memoria. Además, una llamada de función de miembro directo es (potencialmente) más rápida que una llamada de función de miembro virtual. No pesimes prematuramente tu clase al hacer de manera preventiva funciones de miembros virtuales.
3

Por ejemplo, las funciones miembro en Java son 100% virtuales. En C++ se considera una penalización de tiempo de llamada de tamaño de código/función. Además, una función no virtual garantiza que la implementación de la función siempre será la misma (utilizando el objeto/referencia de la clase base). Scott Meyers en "Effective C++" lo analiza con más detalle.

+1

El hecho de que una función no virtual" se mantenga igual "es importante para otras funciones que lo llaman. Es mucho más difícil obtener la función de ** llamada ** cuando hay funciones virtuales involucradas. –

0

Los métodos virtuales son una forma de lograr el polimorfismo. Se usan cuando desea definir alguna acción en un nivel más abstracto, de modo que es imposible implementarlo realmente porque es demasiado general. Solo en las clases derivadas puede decir cómo realizar esa acción. Pero con la definición de un método virtual, usted crea un requisito, que agrega rigidez a la jerarquía de clases. Esto puede ser aconsejable o no, depende de lo que está tratando de obtener y de su propio gusto.

1

Supongo que una posible forma de determinar rápidamente sería considerar si va a tratar con un montón de clases similares que va a utilizar para realizar las mismas tareas, con el cambio siendo el forma que va sobre hacer esas tareas.

Un ejemplo trivial sería el problema de las áreas de cálculo para varias figuras geométricas. Necesita las áreas de cuadrados, círculos, rectángulos, triángulos, etc. y lo único que cambia aquí son las fórmulas matemáticas (el camino) que usa para calcular el área. Por lo tanto, sería una buena decisión tener cada una de estas formas heredando de una clase base común y agregar un método virtual en la clase base que devuelve el área (que luego puede implementar en cada uno de los niños con la fórmula matemática respectiva) .

Hacer todo lo virtual "por si acaso" hará que sus objetos ocupen más memoria. Además, hay una sobrecarga pequeña (pero no cero) al llamar funciones virtuales. Entonces, en mi humilde opinión, hacer que todo sea virtual "por las dudas" sería una mala idea cuando las restricciones de rendimiento/memoria son importantes (lo que básicamente significa en todos los programas del mundo real que se escribe).

Sin embargo, esto nuevamente es discutible en función de cuán claramente se detallan los requisitos y con qué frecuencia se esperan los cambios de código. Por ejemplo, en una herramienta rápida y sucia o un prototipo inicial en el que unos pocos bytes de memoria adicionales y algunos milisegundos de tiempo perdido no significan mucho, estaría bien tener un montón (innecesariamente) de funciones virtuales por el bien de flexibilidad.

4

Es una pregunta difícil. Pero hay algunas pautas/reglas de oro a seguir.

  1. Mientras que no es necesario para derivar de una clase, entonces no escriben cualquier método virtual, una vez que se necesita para derivar, sólo hacen virtual esos métodos necesarias para personalizar la clase hija.
  2. Si una clase tiene un método virtual, entonces el destructor será virtual (fin del debate).
  3. Tratar de seguir el modismo NVI (interfaz no virtual), hacer virtual método no público y proporcionar envoltorios públicos a cargo de evaluar las condiciones previas y posteriores, de modo que las clases derivadas no puedan romperlas accidentalmente.

Creo que son lo suficientemente simples. Definitivamente dejo que el ABI sea parte de la reflexión, solo es útil cuando se entregan archivos DLL.

Cuestiones relacionadas