runtime polymorphism c
Un estudio detallado del polimorfismo en tiempo de ejecución en C ++.
El polimorfismo en tiempo de ejecución también se conoce como polimorfismo dinámico o unión tardía. En el polimorfismo en tiempo de ejecución, la llamada a la función se resuelve en tiempo de ejecución.
Por el contrario, para compilar el tiempo o el polimorfismo estático, el compilador deduce el objeto en tiempo de ejecución y luego decide qué llamada de función enlazar con el objeto. En C ++, el polimorfismo en tiempo de ejecución se implementa mediante la invalidación de métodos.
En este tutorial, exploraremos todo sobre el polimorfismo en tiempo de ejecución en detalle.
=> Consulte TODOS los tutoriales de C ++ aquí.
Lo que vas a aprender:
- Anulación de función
- Función virtual
- Trabajo de mesa virtual y _vptr
- Funciones virtuales puras y clase abstracta
- Destructores virtuales
- Conclusión
- Lectura recomendada
Anulación de función
La anulación de función es el mecanismo mediante el cual una función definida en la clase base se define una vez más en la clase derivada. En este caso, decimos que la función se reemplaza en la clase derivada.
Debemos recordar que la anulación de funciones no se puede realizar dentro de una clase. La función se reemplaza solo en la clase derivada. Por lo tanto, la herencia debe estar presente para la anulación de funciones.
La segunda cosa es que la función de una clase base que estamos anulando debe tener la misma firma o prototipo, es decir, debe tener el mismo nombre, el mismo tipo de retorno y la misma lista de argumentos.
Veamos un ejemplo que demuestra la anulación de métodos.
|_+_|Producción:
Clase :: Base
Clase :: Derivado
En el programa anterior, tenemos una clase base y una clase derivada. En la clase base, tenemos una función show_val que se reemplaza en la clase derivada. En la función principal, creamos un objeto de cada clase Base y Derivada y llamamos a la función show_val con cada objeto. Produce la salida deseada.
El enlace anterior de funciones que utilizan objetos de cada clase es un ejemplo de enlace estático.
Ahora veamos qué sucede cuando usamos el puntero de clase base y asignamos objetos de clase derivados como su contenido.
El programa de ejemplo se muestra a continuación:
|_+_|Producción:
Clase :: Base
Ahora vemos que la salida es 'Class :: Base'. Entonces, independientemente del tipo de objeto que contenga el puntero base, el programa genera el contenido de la función de la clase cuyo tipo es el puntero base. En este caso, también se realiza un enlace estático.
Para hacer que la salida del puntero base, el contenido correcto y el enlace adecuado, optemos por el enlace dinámico de funciones. Esto se logra utilizando el mecanismo de funciones virtuales que se explica en la siguiente sección.
mejor actualizador de controladores para windows 7
Función virtual
Para que la función anulada debe estar vinculada dinámicamente al cuerpo de la función, hacemos que la función de clase base sea virtual usando la palabra clave “virtual”. Esta función virtual es una función que se reemplaza en la clase derivada y el compilador lleva a cabo un enlace dinámico o tardío para esta función.
Ahora modifiquemos el programa anterior para incluir la palabra clave virtual de la siguiente manera:
|_+_|Producción:
Clase :: Derivado
Entonces, en la definición de clase de Base anterior, hicimos que show_val funcione como 'virtual'. Como la función de la clase base se hace virtual, cuando asignamos un objeto de clase derivada al puntero de la clase base y llamamos a la función show_val, el enlace ocurre en tiempo de ejecución.
Por lo tanto, como el puntero de clase base contiene un objeto de clase derivada, el cuerpo de la función show_val en la clase derivada está vinculado a la función show_val y, por lo tanto, a la salida.
En C ++, la función anulada en la clase derivada también puede ser privada. El compilador solo verifica el tipo de objeto en tiempo de compilación y vincula la función en tiempo de ejecución, por lo tanto, no hace ninguna diferencia incluso si la función es pública o privada.
Tenga en cuenta que si una función se declara virtual en la clase base, será virtual en todas las clases derivadas.
Pero hasta ahora, no hemos discutido cómo exactamente las funciones virtuales juegan un papel en la identificación de la función correcta que debe vincularse o, en otras palabras, qué tan tardía ocurre la vinculación.
La función virtual está vinculada al cuerpo de la función con precisión en tiempo de ejecución mediante el uso del concepto de mesa virtual (VTABLE) y un puntero oculto llamado _vptr.
Ambos conceptos son de implementación interna y no pueden ser utilizados directamente por el programa.
destripador de dvd freeware para windows 8
Trabajo de mesa virtual y _vptr
Primero, entendamos qué es una tabla virtual (VTABLE).
El compilador en tiempo de compilación configura un VTABLE cada uno para una clase que tiene funciones virtuales, así como las clases que se derivan de clases que tienen funciones virtuales.
Una VTABLE contiene entradas que son indicadores de función a las funciones virtuales que pueden ser invocadas por los objetos de la clase. Hay una entrada de puntero de función para cada función virtual.
En el caso de funciones virtuales puras, esta entrada es NULL. (Esta es la razón por la que no podemos instanciar la clase abstracta).
La siguiente entidad, _vptr, que se denomina puntero vtable, es un puntero oculto que el compilador agrega a la clase base. Este _vptr apunta a la vtable de la clase. Todas las clases derivadas de esta clase base heredan el _vptr.
Cada objeto de una clase que contiene las funciones virtuales almacena internamente este _vptr y es transparente para el usuario. Cada llamada a una función virtual que usa un objeto se resuelve usando este _vptr.
Tomemos un ejemplo para demostrar el funcionamiento de vtable y _vtr.
|_+_|Producción:
Derived1_virtual :: function1_virtual ()
Base :: function2_virtual ()
En el programa anterior, tenemos una clase base con dos funciones virtuales y un destructor virtual. También hemos derivado una clase de la clase base y en eso; sólo hemos anulado una función virtual. En la función principal, el puntero de clase derivada se asigna al puntero base.
Luego llamamos a ambas funciones virtuales usando un puntero de clase base. Vemos que se llama a la función anulada cuando se llama y no a la función base. Mientras que en el segundo caso, como la función no se anula, se llama a la función de clase base.
Ahora veamos cómo se representa internamente el programa anterior usando vtable y _vptr.
Según la explicación anterior, como hay dos clases con funciones virtuales, tendremos dos vtables, una para cada clase. Además, _vptr estará presente para la clase base.
Arriba se muestra la representación pictórica de cómo será el diseño de vtable para el programa anterior. La vtable para la clase base es sencilla. En el caso de la clase derivada, solo se anula function1_virtual.
Por tanto, vemos que en la clase derivada vtable, el puntero de función para function1_virtual apunta a la función anulada en la clase derivada. Por otro lado, el puntero de función para function2_virtual apunta a una función en la clase base.
Por tanto, en el programa anterior, cuando se asigna al puntero base un objeto de clase derivada, el puntero base apunta a _vptr de la clase derivada.
Entonces, cuando se realiza la llamada b-> function1_virtual (), se llama a function1_virtual de la clase derivada y cuando se realiza la llamada de función b-> function2_virtual (), ya que este puntero de función apunta a la función de clase base, la función de clase base se llama.
Funciones virtuales puras y clase abstracta
Hemos visto detalles sobre funciones virtuales en C ++ en nuestra sección anterior. En C ++, también podemos definir un ' pura función virtual ”Que generalmente se equipara a cero.
La función virtual pura se declara como se muestra a continuación.
|_+_|La clase que tiene al menos una función virtual pura que se denomina ' clase abstracta ”. Nunca podemos crear una instancia de la clase abstracta, es decir, no podemos crear un objeto de la clase abstracta.
Esto se debe a que sabemos que se realiza una entrada para cada función virtual en la VTABLE (tabla virtual). Pero en el caso de una función virtual pura, esta entrada no tiene ninguna dirección, lo que la hace incompleta. Entonces el compilador no permite crear un objeto para la clase con una entrada VTABLE incompleta.
Esta es la razón por la cual no podemos instanciar una clase abstracta.
El siguiente ejemplo demostrará la función virtual pura y la clase abstracta.
|_+_|Producción:
Anulación de la función virtual pura en la clase derivada
En el programa anterior, tenemos una clase definida como Base_abstract que contiene una función virtual pura que la convierte en una clase abstracta. Luego derivamos una clase 'Derived_class' de Base_abstract y anulamos la función virtual pura impresa en ella.
En la función principal, no se comenta esa primera línea. Esto se debe a que si lo descomentamos, el compilador dará un error ya que no podemos crear un objeto para una clase abstracta.
Pero la segunda línea en adelante el código funciona. Podemos crear con éxito un puntero de clase base y luego le asignamos un objeto de clase derivada. A continuación, llamamos a una función de impresión que genera el contenido de la función de impresión anulada en la clase derivada.
Enumeremos brevemente algunas características de la clase abstracta:
- No podemos instanciar una clase abstracta.
- Una clase abstracta contiene al menos una función virtual pura.
- Aunque no podemos instanciar una clase abstracta, siempre podemos crear punteros o referencias a esta clase.
- Una clase abstracta puede tener alguna implementación como propiedades y métodos junto con funciones virtuales puras.
- Cuando derivamos una clase de la clase abstracta, la clase derivada debe anular todas las funciones virtuales puras en la clase abstracta. Si no lo hizo, entonces la clase derivada también será una clase abstracta.
Destructores virtuales
Los destructores de la clase pueden declararse virtuales. Siempre que hacemos upcast, es decir, asignando el objeto de clase derivada a un puntero de clase base, los destructores ordinarios pueden producir resultados inaceptables.
Por ejemplo,considere la siguiente conversión del destructor ordinario.
|_+_|Producción:
Clase base :: Destructor
En el programa anterior, tenemos una clase derivada heredada de la clase base. En general, asignamos un objeto de la clase derivada a un puntero de clase base.
Idealmente, el destructor que se llama cuando se llama a 'delete b' debería haber sido el de la clase derivada, pero podemos ver en la salida que el destructor de la clase base se llama ya que el puntero de la clase base apunta a eso.
Debido a esto, no se llama al destructor de la clase derivada y el objeto de la clase derivada permanece intacto, lo que resulta en una pérdida de memoria. La solución a esto es hacer que el constructor de la clase base sea virtual para que el puntero del objeto apunte al destructor correcto y se lleve a cabo la destrucción adecuada de los objetos.
El uso del destructor virtual se muestra en el siguiente ejemplo.
|_+_|Producción:
Clase derivada :: Destructor
Clase base :: Destructor
Este es el mismo programa que el programa anterior, excepto que hemos agregado una palabra clave virtual delante del destructor de la clase base. Al hacer virtual el destructor de la clase base, hemos logrado el resultado deseado.
Podemos ver que cuando asignamos un objeto de clase derivada al puntero de clase base y luego eliminamos el puntero de clase base, los destructores se llaman en el orden inverso al de la creación del objeto. Esto significa que primero se llama al destructor de la clase derivada y el objeto se destruye y luego se destruye el objeto de la clase base.
cómo instalar appium en windows
Nota: En C ++, los constructores nunca pueden ser virtuales, ya que los constructores participan en la construcción e inicialización de los objetos. Por lo tanto, necesitamos que todos los constructores se ejecuten por completo.
Conclusión
El polimorfismo en tiempo de ejecución se implementa mediante la invalidación de métodos. Esto funciona bien cuando llamamos a los métodos con sus respectivos objetos. Pero cuando tenemos un puntero de clase base y llamamos a métodos anulados utilizando el puntero de clase base que apunta a los objetos de la clase derivada, se producen resultados inesperados debido a la vinculación estática.
Para superar esto, utilizamos el concepto de funciones virtuales. Con la representación interna de vtables y _vptr, las funciones virtuales nos ayudan a llamar con precisión las funciones deseadas. En este tutorial, hemos visto en detalle sobre el polimorfismo en tiempo de ejecución utilizado en C ++.
Con esto, concluimos nuestros tutoriales sobre programación orientada a objetos en C ++. Esperamos que este tutorial sea útil para obtener una mejor comprensión de los conceptos de programación orientada a objetos en C ++.
=> Visite aquí para aprender C ++ desde cero.
Lectura recomendada
- Polimorfismo en C ++
- Herencia en C ++
- Funciones de amigo en C ++
- Clases y objetos en C ++
- Uso de la clase selecta de selenio para manejar elementos desplegables en una página web - Tutorial de selenio n. ° 13
- Tutorial de la función principal de Python con ejemplos prácticos
- Máquina virtual Java: cómo JVM ayuda a ejecutar aplicaciones Java
- Cómo configurar los archivos de secuencia de comandos de LoadRunner VuGen y la configuración del tiempo de ejecución