From 0eeb9e06262a4ce703a5d4c59b007ebb6fbf27f9 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Mon, 12 Nov 2018 18:53:53 +0100 Subject: [PATCH] Tarea dispatching --- dispatching-task.tex | 110 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 dispatching-task.tex diff --git a/dispatching-task.tex b/dispatching-task.tex new file mode 100644 index 0000000..f596e8e --- /dev/null +++ b/dispatching-task.tex @@ -0,0 +1,110 @@ +\documentclass[a4paper]{article} + +\usepackage[utf8]{inputenc} +\usepackage[spanish]{babel} +\usepackage{listings} +\usepackage{url} +\usepackage{bookmark} + +\lstset{ + basicstyle=\ttfamily\footnotesize, + tabsize=4, +} + +%La tarea consiste en documentar de forma breve (seis páginas máximo) la diferencia entre static y dynamic dispatching de métodos en los lenguajes orientados a objetos. Al menos que aparezcan tres lenguajes de programación distintos. +%Un buen ejemplo es C++, que tiene enlace estático por defecto pero las palabras virtual y override permiten enlace dinámico. En C# se añade además la palabra dynamic que permite un mecanismo mucho más general de dynamic dispatching. Hay lenguajes que solo admiten dynamic dispatching como Java, Javascript o Python. +%Podéis incluir algún ejemplo de cómo se definen en esos lenguajes. +%Y es importante documentar las fuentes de donde habéis obtenido la información. + +\title{Static vs dynamic dispatching a través de ejemplos en lenguajes orientados a objetos} +\author{Carlos S. Galindo Jiménez} + +\begin{document} +\maketitle + +\section{Introducción} +En lenguajes de programación orientados a objetos y que incluyen polimorfismo, se ha de tomar una decisión de diseño respecto al procedimiento de llamada a un método. Esto se denomina en inglés como ``dispatching'', y las dos formas más típicas son el dispatching dinámico y el estático. El primero se resuelve en tiempo de ejecución, normalmente buscando en la memoria asociada al objeto una referencia al método correspondiente. El segundo simplemente compila la llamada como un salto al código directamente. + +En el resto del documento se va a describir de forma más específica las opciones de dispatching que ofrecen distintos lenguajes de programación orientados a objetos. + +\section{Java} +En Java, las llamadas a métodos se compilan a distintas instrucciones en bytecode, en función del tipo de método que es llamado. Las dos llamadas más comunes son aquellas que emplean la instrucción bytecode ``invokevirtual'' y ``invokestatic''. Tal y como indican sus nombres, la primera invoca un método virtual (empleando dynamic dispatching) y la segunda, un método estático de una clase dada como argumento. También existen otras para casos especiales, como las llamadas a funciones lambda, métodos de interfaces, constructores o métodos privados. + +En el primer caso, la especificación de la Java Virtual Machine (JVM) no especifica el formato interno de los objetos, ni cómo debe ser llamado un método, simplemente especifica lo que debería ocurrir en cada caso. Por ello, la llamada podría producir de múltiples modos. En concreto, la implementación de Oracle representa objetos con un bloque de memoria para sus atributos y una tabla virtual con la referencia a sus métodos. De este modo, invocar un método sobre un objeto no requiere obtener su tipo de forma dinámica, sino que toda la información de la clase a la que pertenece se encuentra en su representación interna. + +En el caso de los métodos estáticos, se pueden llamar tanto sobre una clase o sobre un objeto (también, de forma implícita sobre \texttt{this}). En cualquier caso, durante la compilación a bytecode se simplifica la llamada a una sola instrucción, cuyos argumentos son la clase y el nombre del método. El tipo dinámico del objeto no es determinado, por lo que aunque un objeto podría ser de un subtipo, se llama al método de la clase del tipo aparente. + +En el siguiente ejemplo de StackOverflow\footnote{\url{https://stackoverflow.com/a/46504885}} se puede apreciar las dos variantes: + +\begin{lstlisting}[language=Java] +public class Parent { + public static void statictest() { + System.out.println("Parent.statictest"); + } + public void test() { + System.out.println("Parent.test"); + } +} +public class Child extends Parent { + public static void statictest() { + System.out.println("Child.statictest"); + } + public void test() { + System.out.println("Child.test"); + } +} +public static void main(String[] args) { + Child c=new Child(); + Parent p=c; + c.test(); + p.test(); + c.statictest(); + p.statictest(); +} +\end{lstlisting} + +El resultado de las dos primeras llamadas (que usan dynamic dispatching) sería \texttt{Child.test}, pero las llamadas estáticas serán \texttt{Child} y \texttt{Parent.statictest} respectivamente, mostrando que las llamadas a métodos estáticas se resuelven mediande static dispatching, o lo que es lo mismo en tiempo de compilación. + +El funcionamiento de otros lenguajes que se compilan a Java Bytecode, como Scala o Kotlin puede variar, puesto que la especificación de la JVM no especifica un estilo concreto para cada tipo de llamada. La decisión queda en la especificación del lenguaje o en el compilador. + +\subsection*{Fuentes} +La información para esta sección ha sido extraída de la especificación del lenguaje Java y de la Java Virtual Machine, que se puede encontrar en la página web de Oracle (\url{https://docs.oracle.com/javase/specs/}). +\newpage + +\section{C++} +Así como en Java se emplea dynamic dispatching para la llamada de métodos por defecto, C++ presenta el caso contrario: el dispatching es estático por defecto, salvo que se emplee la palabra \texttt{virtual} en la cabecera del método. Obviamente, esto es prerrequisito para poder sobreescribirlo en una clase hija. Esto da un control más granular al programador, puesto que las llamadas realizadas con static dispatching son más rápidas de ejecutar. De este modo, todas las llamadas son más rápidas, excepto aquellas para las que es absolutamente necesario el dynamic dispatching. La implementación es similar a la de Java, empleando tablas virtuales (v-tables), pero generándolas únicamente cuando existen funciones virtuales. + +Del mismo modo que en el ejemplo de Java, aquellas llamadas que sean estáticas dependerán del tipo estático de la variable, y las dinámicas del tipo en tiempo de ejecución. Tomemos como ejemplo el siguiente programa: + +\begin{lstlisting}[language=C++] +class Shape { +public: + int classId() { return -1; } + virtual int area() { return 0; } +} +public Square : public Shape { +public: + int side; + int classId() { return -2; } + int area() { + return side * side; + } +} +\end{lstlisting} + +Con un objeto de tipo \texttt{Square}, las llamadas devolverían -2 y el área correspondiente, pero en caso de encapsularlo al supertipo \texttt{Shape}, la llamada a ``classId'' devolvería -1. Nótese que la palabra clave \texttt{virtual} sólo es necesaria en métodos que se van a sobreescribir, y no necesariamente en métodos que sobreescriben a otro. + +Por último, para forzar static dispatching incluso en un método virtual, se puede llamar con el operador \texttt{::} en vez de \texttt{->}. + +\subsection*{Fuentes} +Todo esto queda detallado en la especificación de C++ de Microsoft, específicamente de la descripción de \texttt{virtual} (\url{https://docs.microsoft.com/en-us/cpp/cpp/virtual-functions?view=vs-2017}) +\newpage + +\section{C\#} +C\# se comporta de un modo similar a C++, pero con algunas particularidades que difieren. Por ejemplo, la palabra clave \texttt{virtual} no es compatible con métodos abstractos, privados o estáticos. Además, como los accesores y escritores a propiedades de un objeto se declaran con la propiedad, se puede emplear \texttt{virtual} sobre atributos de una clase. C\# también introduce otras características a la hora de sobreescribir funciones y ocultarlas, con palabras clave como \texttt{new} y \texttt{override}. Esto complica la sobreescritura, pero la base de dispatching es similar. + +La implementación interna usa v-tables de nuevo, de forma muy similar que C++. El ejemplo de código y su resultado es bastante similar al de C++ (obviando pequeños cambios de sintaxis), por lo que vamos a omitirlo. + +\subsection*{Fuentes} +La información de esta sección ha sido extraída de la especificación de lenguaje de C\#, que se encuentra en \url{https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/index}. +\end{document} \ No newline at end of file