Métodos virtuales

Los métodos virtuales son una característica importante en la programación orientada a objetos (POO). Permiten que las clases base definan métodos que pueden ser redefinidos por las clases derivadas, lo que permite lograr el polimorfismo y la vinculación dinámica en tiempo de ejecución. Aquí tienes los pasos para usar métodos virtuales en C++:

Declaración en la clase base.- En la clase base, declara el método que deseas que sea virtual. Para hacerlo, coloca la palabra clave virtual antes de la declaración del método. Por ejemplo:

Redefinición en la clase derivada.- En las clases derivadas que heredan de la clase base, puedes redefinir el método virtual. Esto se logra declarando el mismo método con la misma firma (nombre y parámetros) en la clase derivada. Por ejemplo:

Uso del polimorfismo.- Puedes utilizar el polimorfismo para invocar el método virtual en tiempo de ejecución. Esto se hace a través de un puntero o referencia a la clase base que apunte a un objeto de la clase derivada. Por ejemplo:

En este caso, se crea un objeto de la clase derivada que nombraremos Derivada y se asigna su dirección de memoria a un puntero de tipo Base*.

Luego, al llamar al método metodoVirtual() a través del puntero ptr, se ejecuta la implementación del método en la clase derivada.

Los métodos virtuales permiten el polimorfismo, lo que significa que el comportamiento del método se determina en tiempo de ejecución en función del tipo real del objeto en lugar del tipo estático del puntero o referencia. Esto proporciona flexibilidad y extensibilidad en el diseño de clases y permite la implementación de interfaces comunes a través de herencia y redefinición de métodos.

Implementación de los métodos virtuales

En C++, los métodos virtuales se implementan utilizando una tabla de punteros a funciones llamada tabla de funciones virtuales o tabla de punteros virtuales (vtable). Cada clase que tiene al menos un método virtual tiene su propia vtable.

La vtable es una estructura de datos interna que almacena los punteros a las funciones virtuales de una clase. La vtable se crea durante la compilación y se almacena como un miembro oculto en cada objeto de la clase. Cada objeto de la clase contiene un puntero a su vtable correspondiente.

Cuando se llama a un método virtual en un objeto, en lugar de acceder directamente a la dirección de la función, el compilador utiliza el puntero a la vtable del objeto para encontrar la dirección correcta de la función virtual correspondiente a ese método en particular. Esto permite lograr la vinculación dinámica, lo que significa que el método llamado se resuelve en tiempo de ejecución en función del tipo real del objeto, en lugar del tipo estático del puntero o referencia utilizado.

La vtable es creada por el compilador y es invisible para el programador. El proceso de resolución de la función virtual y acceso a la vtable se realiza automáticamente por el compilador y el tiempo de ejecución del programa.

Es importante destacar que las clases que tienen métodos virtuales incurren en una pequeña sobrecarga de memoria y rendimiento debido al almacenamiento de la vtable y el acceso a ella en tiempo de ejecución. Sin embargo, esta sobrecarga es mínima y generalmente no es un problema a menos que se trate de una aplicación con requisitos de rendimiento muy exigentes.

En resumen, los métodos virtuales en C++ se implementan utilizando una tabla de punteros a funciones llamada vtable, que se almacena como un miembro oculto en cada objeto de la clase. Esto permite la vinculación dinámica y la resolución de métodos en tiempo de ejecución en función del tipo real del objeto.

Constructores virtuales

En C++, no existen constructores virtuales como tal. Los constructores no pueden ser declarados como virtuales en la sintaxis del lenguaje. Sin embargo, hay una manera de lograr un comportamiento similar utilizando técnicas como la creación de un constructor protegido o el uso de un patrón de diseño como el patrón de fábrica.

Aquí hay un ejemplo que ilustra cómo se puede lograr un comportamiento similar a un constructor virtual utilizando un constructor protegido y un método estático de creación:

En este ejemplo, las clases Base y Derivada tienen un constructor protegido…

…y un método estático de creación llamado crearInstancia().

Estos métodos estáticos se utilizan para crear instancias de las clases y asegurarse de que solo se puedan crear a través de esos métodos. Cada clase tiene su propia implementación del método imprimir(), que se llama según el tipo real del objeto en tiempo de ejecución.

Cabe mencionar que este enfoque no es exactamente igual a un constructor virtual, ya que no se realiza una construcción dinámica de objetos en la jerarquía de clases. Sin embargo, puede proporcionar un comportamiento similar en el sentido de que se puede controlar la creación de objetos a través de métodos estáticos y permitir la redefinición del comportamiento a través de métodos virtuales.

Destructores virtuales

En C++, los destructores virtuales son una herramienta importante para garantizar una correcta liberación de recursos cuando se trabaja con clases polimórficas. Un destructor virtual es aquel declarado en la clase base como virtual, lo que permite que sea sobrescrito en las clases derivadas y llamado de manera adecuada durante la destrucción de un objeto polimórfico. Aquí tienes un ejemplo que ilustra el uso de destructores virtuales:

En este ejemplo, la clase que nominaremos como Base declara su destructor como virtual mediante la palabra clave virtual ~Base().

La clase Derivada hereda de la clase Base y también tiene su propio destructor ~Derivada().

Cuando se crea un objeto de la clase Derivada y se asigna a un puntero de tipo Base*, se llama al destructor virtual ~Base() durante la eliminación del objeto. Gracias a la declaración virtual, el destructor adecuado (~Derivada()) se ejecuta en lugar del destructor base.