Sé que la final
palabra clave se usa para evitar que las clases derivadas anulen el método virtual. Sin embargo, no puedo encontrar ningún ejemplo útil cuando realmente debería usar final
palabras clave con virtual
método. Aún más, parece que el uso de final
métodos virtuales es un mal olor ya que no permite a los programadores extender la clase en el futuro.
Mi pregunta es la siguiente:
¿Hay algún caso útil cuando realmente debería usar final
en la virtual
declaración del método?
Respuestas:
Un resumen de lo que hace la
final
palabra clave: Digamos que tenemos una clase baseA
y una clase derivadaB
. La funciónf()
puede declararseA
comovirtual
, lo que significa que la claseB
puede anularla. Pero entonces claseB
podría desear que cualquier clase que se deriva más lejosB
no pueda ser anuladaf()
. Ahí es cuando tenemos que declararf()
comofinal
enB
. Sin lafinal
palabra clave, una vez que definimos un método como virtual, cualquier clase derivada sería libre de anularlo. Lafinal
palabra clave se usa para poner fin a esta libertad.Un ejemplo de por qué necesitaría tal cosa: suponga que la clase
A
define una función virtualprepareEnvelope()
, y la clase laB
anula y la implementa como una secuencia de llamadas a sus propios métodos virtualesstuffEnvelope()
,lickEnvelope()
ysealEnvelope()
. La claseB
tiene la intención de permitir que las clases derivadas anulen estos métodos virtuales para proporcionar sus propias implementaciones, pero la claseB
no quiere permitir que ninguna clase derivada anuleprepareEnvelope()
y, por lo tanto, cambie el orden de las cosas, lame, selle u omita la invocación de una de ellas. Entonces, en este caso, la clase seB
declaraprepareEnvelope()
como final.fuente
class B might wish that any class which is further derived from B should not be able to override f()
? ¿Hay algún caso en el mundo real donde tal clase B y método f () exista y encaje bien?A menudo es útil desde una perspectiva de diseño poder marcar cosas como inmutables. Del mismo modo,
const
proporciona protectores del compilador e indica que un estado no debe cambiar,final
puede usarse para indicar que el comportamiento no debería cambiar más abajo en la jerarquía de herencia.Ejemplo
Considere un videojuego donde los vehículos llevan al jugador de un lugar a otro. Todos los vehículos deben verificar para asegurarse de que están viajando a un lugar válido antes de la salida (asegurándose de que la base en el lugar no esté destruida, por ejemplo). Podemos comenzar usando el lenguaje de interfaz no virtual (NVI) para garantizar que esta verificación se realice independientemente del vehículo.
Ahora digamos que tenemos vehículos voladores en nuestro juego, y algo que todos los vehículos voladores requieren y tienen en común es que deben pasar por una inspección de seguridad dentro del hangar antes del despegue.
Aquí podemos usar
final
para garantizar que todos los vehículos voladores pasarán por dicha inspección y también comunicar este requisito de diseño de los vehículos voladores.Al usarlo
final
de esta manera, estamos efectivamente ampliando la flexibilidad del lenguaje de interfaz no virtual para proporcionar un comportamiento uniforme en la jerarquía de herencia (incluso como una ocurrencia tardía, contrarrestando el frágil problema de la clase base) a las funciones virtuales. Además, nos compramos espacio de maniobra para realizar cambios centrales que afectan a todos los tipos de vehículos voladores como una ocurrencia tardía sin modificar cada una de las implementaciones de vehículos voladores que existen.Este es un ejemplo de uso
final
. Hay contextos que encontrará en los que simplemente no tiene sentido que una función miembro virtual se anule aún más; hacerlo podría conducir a un diseño frágil y una violación de sus requisitos de diseño.Ahí es donde
final
es útil desde una perspectiva de diseño / arquitectura.También es útil desde la perspectiva de un optimizador, ya que proporciona al optimizador esta información de diseño que le permite desvirtualizar las llamadas a funciones virtuales (eliminando la sobrecarga de despacho dinámico y, a menudo, de manera más significativa, eliminando una barrera de optimización entre la persona que llama y la persona que llama).
Pregunta
De los comentarios:
No tiene sentido que una clase base en la raíz de una jerarquía declare una función como ambos
virtual
yfinal
. Eso me parece bastante tonto, ya que haría que tanto el compilador como el lector humano tengan que saltar a través de aros innecesarios que se pueden evitar simplemente evitandovirtual
directamente en tal caso. Sin embargo, las subclases heredan funciones de miembros virtuales de la siguiente manera:En este caso,
Bar::f
usar explícitamente o no la palabra clave virtual,Bar::f
es una función virtual. Lavirtual
palabra clave se convierte en opcional en este caso. Por lo que podría tener sentido paraBar::f
ser especificado comofinal
, a pesar de que es una función virtual (final
puede solamente ser utilizado para funciones virtuales).Y algunas personas pueden preferir, estilísticamente, indicar explícitamente que
Bar::f
es virtual, así:Para mí es algo redundante usar ambos
virtual
y losfinal
especificadores para la misma función en este contexto (del mismo modovirtual
yoverride
), pero es una cuestión de estilo en este caso. Algunas personas pueden encontrar quevirtual
aquí se comunica algo valioso, al igual que el usoextern
para declaraciones de funciones con enlace externo (a pesar de que es opcional sin otros calificadores de enlace).fuente
private
método dentro de tuVehicle
clase? ¿No quisiste decir en suprotected
lugar?private
no se extiende a la anulación (un poco contra-intuitivo). Los especificadores públicos / protegidos / privados para funciones virtuales solo se aplican a las personas que llaman y no a los anuladores, en pocas palabras. Una clase derivada puede anular las funciones virtuales de su clase base independientemente de su visibilidad.protected
podría tener un sentido un poco más intuitivo. Simplemente prefiero alcanzar la menor visibilidad posible siempre que puedo. Creo que la razón por la que el lenguaje está diseñado de esta manera es que, de lo contrario, las funciones privadas virtuales de los miembros no tendrían ningún sentido fuera del contexto de la amistad, ya que ninguna clase sino un amigo podría anularlas si los especificadores de acceso importaran en el contexto de anular y no solo llamar.protected
para evitar confundir a los demás. Terminé poniendo un comentario, al menos, describiendo cómo las funciones virtuales privadas todavía se pueden anular.Permite muchas optimizaciones, porque puede conocerse en tiempo de compilación qué función se llama.
Tenga cuidado al lanzar la palabra "olor a código". "final" no hace imposible extender la clase. Haga doble clic en la palabra "final", presione la tecla de retroceso y extienda la clase. SIN EMBARGO final es una excelente documentación de que el desarrollador no espera que anule la función, y que por eso el próximo desarrollador debe tener mucho cuidado, porque la clase podría dejar de funcionar correctamente si el método final se anula de esa manera.
fuente
final
yvirtual
al mismo tiempo?final
yoverride
.final
realmente puede ayudarlos