Sé que la finalpalabra 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 finalpalabras clave con virtualmétodo. Aún más, parece que el uso de finalmé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 finalen la virtualdeclaración del método?

Respuestas:
Un resumen de lo que hace la
finalpalabra clave: Digamos que tenemos una clase baseAy una clase derivadaB. La funciónf()puede declararseAcomovirtual, lo que significa que la claseBpuede anularla. Pero entonces claseBpodría desear que cualquier clase que se deriva más lejosBno pueda ser anuladaf(). Ahí es cuando tenemos que declararf()comofinalenB. Sin lafinalpalabra clave, una vez que definimos un método como virtual, cualquier clase derivada sería libre de anularlo. Lafinalpalabra clave se usa para poner fin a esta libertad.Un ejemplo de por qué necesitaría tal cosa: suponga que la clase
Adefine una función virtualprepareEnvelope(), y la clase laBanula y la implementa como una secuencia de llamadas a sus propios métodos virtualesstuffEnvelope(),lickEnvelope()ysealEnvelope(). La claseBtiene la intención de permitir que las clases derivadas anulen estos métodos virtuales para proporcionar sus propias implementaciones, pero la claseBno 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 seBdeclaraprepareEnvelope()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,
constproporciona protectores del compilador e indica que un estado no debe cambiar,finalpuede 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
finalpara 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
finalde 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
finales ú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
virtualyfinal. 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 evitandovirtualdirectamente en tal caso. Sin embargo, las subclases heredan funciones de miembros virtuales de la siguiente manera:En este caso,
Bar::fusar explícitamente o no la palabra clave virtual,Bar::fes una función virtual. Lavirtualpalabra clave se convierte en opcional en este caso. Por lo que podría tener sentido paraBar::fser especificado comofinal, a pesar de que es una función virtual (finalpuede solamente ser utilizado para funciones virtuales).Y algunas personas pueden preferir, estilísticamente, indicar explícitamente que
Bar::fes virtual, así:Para mí es algo redundante usar ambos
virtualy losfinalespecificadores para la misma función en este contexto (del mismo modovirtualyoverride), pero es una cuestión de estilo en este caso. Algunas personas pueden encontrar quevirtualaquí se comunica algo valioso, al igual que el usoexternpara declaraciones de funciones con enlace externo (a pesar de que es opcional sin otros calificadores de enlace).fuente
privatemétodo dentro de tuVehicleclase? ¿No quisiste decir en suprotectedlugar?privateno 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.protectedpodrí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.protectedpara 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
finalyvirtualal mismo tiempo?finalyoverride.finalrealmente puede ayudarlos