Pregunta

En C ++, tengo que especificar explícitamente la palabra clave 'virtual' para hacer que una función miembro sea 'reemplazable', ya que implica una sobrecarga de crear tablas virtuales y vpointers, cuando una función miembro se hace reemplazable (por lo que cada función miembro está implícitamente no anulable por razones de rendimiento).

También permite ocultar una función miembro (si no se anula) cuando una subclase proporciona una implementación separada con el mismo nombre y firma.

La misma técnica también se usa en C #. Me pregunto por qué Java se alejó de este comportamiento e hizo que todos los métodos fueran anulables por defecto y proporcionara la capacidad de deshabilitar el comportamiento de anulación en el uso explícito de la palabra clave 'final'.

¿Fue útil?

Solución

La mejor pregunta podría ser "¿Por qué C # tiene métodos no virtuales?" O al menos, ¿por qué no son virtuales de forma predeterminada con la opción de marcarlos como no virtuales?

En C ++, existe la idea (como señaló Brian tan amablemente) de que si no la quieres, no la pagas. El problema es que si lo quieres, esto generalmente significa que terminas pagando por la nariz. En la mayoría de las implementaciones de Java, están diseñadas explícitamente para muchas llamadas virtuales; Las implementaciones de vtable tienden a ser rápidas, apenas más caras que las llamadas no virtuales, lo que significa que se pierde la ventaja principal de las funciones no virtuales. Además, los compiladores JIT pueden incorporar funciones virtuales en tiempo de ejecución. Como tal, por razones de eficiencia, hay muy pocas razones para utilizar funciones no virtuales.

Por lo tanto, se reduce en gran medida al principio de menor sorpresa. Nos dice que todos los métodos deben comportarse de la misma manera, no la mitad de ellos son virtuales y la otra mitad no virtuales. Dado que necesitamos tener al menos algunos métodos virtuales para lograr esta cosa del polimorfismo, tiene sentido que todos sean virtuales. Además, tener dos métodos con la misma firma es pedirle que se dispare en el pie.

El polimorfismo también dicta que el objeto mismo debe tener control sobre lo que hace. Su comportamiento no debe determinarse si el cliente piensa que es un FooParent o un FooChild.

EDITAR: Así que me llaman mis afirmaciones. El siguiente párrafo es una conjetura de mi parte, no una declaración de hechos.

Un efecto secundario interesante de todo esto es que los programadores de Java tienden a usar interfaces muy fuertemente. Dado que las optimizaciones de métodos virtuales hacen que el costo de las interfaces sea prácticamente inexistente, le permiten usar una Lista (por ejemplo) en lugar de una ArrayList, y cambiarla por una LinkedList en una fecha posterior con un simple cambio de una línea y sin penalización adicional.

EDITAR: También pongo un par de fuentes. Si bien no son las fuentes originales, provienen de Sun explicando algunos de los trabajos en HotSpot.

Inlining

VTable

Otros consejos

Tomado de aquí (# 34)

  

No hay una palabra clave virtual en Java   porque todos los métodos no estáticos siempre   usar enlace dinámico. En Java, el   programador no tiene que decidir   si usar enlace dinámico. los   razón virtual existe en C ++ es por lo que   puede dejarlo para un ligero aumento   en eficiencia cuando estás sintonizando   rendimiento (o, dicho de otro modo, " Si   no lo usas, no pagas por   it "), que a menudo resulta en confusión   y sorpresas desagradables. El final   la palabra clave proporciona cierta libertad para   ajuste de eficiencia & # 8211; le dice al   compilador de que este método no puede ser   anulado, y así puede ser   estáticamente vinculado (y hecho en línea,   utilizando así el equivalente de un C ++   llamada no virtual). Estas optimizaciones   depende del compilador.

Un poco circular, tal vez.

Entonces, la lógica de Java es probablemente algo como esto: el objetivo de un lenguaje orientado a objetos es que las cosas pueden extenderse. Entonces, en términos de diseño puro, realmente tiene poco sentido tratar extensible como el "caso especial".

Recuerde que Java tiene el lujo de compilar en tiempo de ejecución. Entonces, algunos de los argumentos de rendimiento en la compilación de C ++ salen por la ventana. En C ++, si una clase podría ser anulada, entonces el compilador tiene que tomar pasos adicionales. En Java, no hay ningún misterio al respecto: en cualquier momento dado, la JVM sabe si un método / clase particular ha sido anulado o no, y eso es esencialmente lo que cuenta.

Tenga en cuenta que la palabra clave final trata esencialmente del diseño del programa, no de la optimización. ¡La JVM no necesita esta información para ver si una clase / método ha sido anulado o no!

Si la pregunta está por preguntar cuál es el mejor enfoque entre java y C ++ / C #, entonces ya se discutió en la dirección opuesta en otro hilo, y muchos recursos disponibles en la red

¿Por qué C # implementa métodos como no virtuales de forma predeterminada?

http://www.artima.com/intv/nonvirtual.html

La reciente introducción de la anotación @Override y su amplia adopción en el nuevo código, sugiere que la respuesta exacta a la pregunta "¿Por qué todos los métodos de Java son invalidables implícitamente?" es de hecho porque el diseñador cometió un error. (Y ya lo arreglaron)

¡Oh! Voy a obtener un voto negativo por esto.

Java intenta acercarse a una definición de lenguaje más dinámica, donde todo es un objeto y todo es un método virtual. También quiere evitar ambigüedades y construcciones difíciles de entender, lo que los diseñadores consideran un defecto en C ++, por lo tanto, no sobrecarga del operador y, en este caso, no tiene la capacidad de tener dos firmas de métodos públicos en una jerarquía de clase que invoque diferentes métodos según el tipo de la variable que hace referencia a ella.

C # está más preocupado por la estabilidad de las subclases y por asegurarse de que las subclases se comporten de manera predecible. C ++ está preocupado por el rendimiento.

Tres prioridades de diseño diferentes, lo que lleva a diferentes opciones.

Diría que en Java el costo del método virtual es bajo en comparación con el costo total de la VM. En C ++ es un costo significativo, en comparación con el fondo C de tipo ensamblado. Nadie decidiría hacer todos los métodos llamados a través del puntero por defecto como resultado de la migración de C a C ++. Es un cambio demasiado grande.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top