¿Cuál es el propósito de la final
palabra clave en C ++ 11 para funciones? Entiendo que evita la anulación de funciones por clases derivadas, pero si este es el caso, ¿no es suficiente declarar como no virtuales sus final
funciones? ¿Hay otra cosa que me falta aquí?
143
virtual
palabra clave o no.func
no es virtual, por lo que no hay nada que anular y, por lo tanto, nada que marcar comooverride
ofinal
.Respuestas:
Lo que se está perdiendo, como idljarn ya mencionado en un comentario es que si está anulando una función de una clase base, entonces no puede marcarla como no virtual:
fuente
virtual
puede causar errores, y C ++ 11 agregó laoverride
etiqueta a una función que detectará esa situación y no se compilará cuando una función que está destinada a anularse realmente se ocultaEs para evitar que una clase sea heredada. De Wikipedia :
También se usa para marcar una función virtual para evitar que se anule en las clases derivadas:
Wikipedia además hace un punto interesante :
Eso significa que se permite lo siguiente:
fuente
"final" también permite una optimización del compilador para evitar la llamada indirecta:
con "final", el compilador puede llamar
CDerived::DoSomething()
directamente desde dentroBlah()
, o incluso en línea. Sin ella, debe generar una llamada indirecta dentro deBlah()
porqueBlah()
podría llamarse dentro de una clase derivada que se ha anuladoDoSomething()
.fuente
Nada que agregar a los aspectos semánticos de "final".
Pero me gustaría agregar al comentario de Chris Green que "final" podría convertirse en una técnica de optimización del compilador muy importante en un futuro no muy lejano. No solo en el caso simple que mencionó, sino también para jerarquías de clase real más complejas que pueden ser "cerradas" por "final", permitiendo así que los compiladores generen un código de despacho más eficiente que con el enfoque vtable habitual.
Una desventaja clave de vtables es que para cualquier objeto virtual de este tipo (suponiendo 64 bits en una CPU Intel típica), el puntero solo consume el 25% (8 de 64 bytes) de una línea de caché. En el tipo de aplicaciones que disfruto escribir, me duele mucho. (Y desde mi experiencia, es el argumento # 1 contra C ++ desde un punto de vista de rendimiento purista, es decir, por programadores de C).
En las aplicaciones que requieren un rendimiento extremo, lo que no es tan inusual para C ++, esto podría llegar a ser increíble, ya que no requiere solucionar este problema manualmente en estilo C o malabarismo de plantilla extraño.
Esta técnica se conoce como Desvirtualización . Un término que vale la pena recordar. :-)
Hay un gran discurso reciente de Andrei Alexandrescu que explica muy bien cómo puede solucionar tales situaciones hoy y cómo "final" podría ser parte de resolver casos similares "automáticamente" en el futuro (discutido con los oyentes):
http://channel9.msdn.com/Events/GoingNative/2013/Writing-Quick-Code-in-Cpp-Quickly
fuente
Final no se puede aplicar a funciones no virtuales.
No sería muy significativo poder marcar un método no virtual como 'final'. Dado
a->foo()
siempre llamareA::foo
.Pero, si A :: foo lo fuera
virtual
, entonces B :: foo lo anularía. Esto podría ser indeseable y, por lo tanto, tendría sentido hacer que la función virtual sea final.La pregunta es, sin embargo, por qué permitir final en funciones virtuales. Si tienes una jerarquía profunda:
Luego,
final
pone un 'piso' sobre la cantidad de anulación que se puede hacer. Otras clases pueden extender A y B y anular susfoo
, pero si una clase extiende C, entonces no está permitido.Por lo tanto, probablemente no tenga sentido hacer el foo de "nivel superior"
final
, pero podría tener sentido más abajo.(Sin embargo, creo que hay espacio para extender las palabras final y anular a los miembros no virtuales. Sin embargo, tendrían un significado diferente).
fuente
final
. Por ejemplo, si usted sabe que quiere todos losShape
s afoo()
-algo predefinido y definida que ninguna forma derivada debe modificar. ¿O me equivoco y hay un patrón mejor para ese caso? EDITAR: ¿Oh, tal vez porque en ese caso, uno simplemente no debería llegar al nivel superiorfoo()
virtual
para empezar? Pero aún así, puede ocultarse, incluso si se llama correctamente (polimórficamente) a través deShape*
...Un caso de uso para la palabra clave 'final' que me gusta es el siguiente:
fuente
final
agrega una intención explícita de no anular su función, y provocará un error del compilador si esto se viola:Tal como está el código, se compila y se
B::foo
anulaA::foo
.B::foo
También es virtual, por cierto. Sin embargo, si cambiamos el # 1 avirtual int foo() final
, entonces este es un error del compilador, y no podemos anularA::foo
más en las clases derivadas.Tenga en cuenta que esto no nos permite "reabrir" una nueva jerarquía, es decir, no hay forma de crear
B::foo
una nueva función no relacionada que pueda estar independientemente al frente de una nueva jerarquía virtual. Una vez que una función es final, nunca se puede volver a declarar en ninguna clase derivada.fuente
La palabra clave final le permite declarar un método virtual, anularlo N veces y luego ordenar que 'esto ya no se pueda anular'. Sería útil para restringir el uso de tu clase derivada, para que puedas decir "Sé que mi superclase te permite anular esto, pero si quieres derivar de mí, ¡no puedes!".
Como señalaron otros carteles, no se puede aplicar a funciones no virtuales.
Un propósito de la palabra clave final es evitar la anulación accidental de un método. En mi ejemplo, DoStuff () puede haber sido una función auxiliar que la clase derivada simplemente necesita cambiar de nombre para obtener el comportamiento correcto. Sin final, el error no se descubriría hasta la prueba.
fuente
La palabra clave final en C ++ cuando se agrega a una función, evita que una clase base la anule. Además, cuando se agrega a una clase, se evita la herencia de cualquier tipo. Considere el siguiente ejemplo que muestra el uso del especificador final. Este programa falla en la compilación.
También:
fuente
Suplemento a la respuesta de Mario Knezović:
El código anterior muestra la teoría, pero en realidad no se probó en compiladores reales. Aprecio mucho si alguien pega una salida desmontada.
fuente