Historia de la cuestión:
El polimorfismo se ha convertido en una de las características clave de la programación orientada a objetos desde las primeras etapas del desarrollo de C++. El objetivo es poder acceder a los objetos a través de una interfaz base sin preocuparse por el tipo concreto. Esto amplía significativamente la expresividad y la flexibilidad del código.
Problema:
Sin polimorfismo, el código se vuelve inflexible: es necesario conocer explícitamente los tipos de los objetos, utilizar switch/case y realizar conversiones de tipo manualmente. Esto complica el mantenimiento y la extensibilidad de la aplicación, ya que agregar nuevos tipos se vuelve costoso o imposible sin modificar el código existente.
Solución:
En C++, el polimorfismo se logra mediante el uso de funciones virtuales. Las clases declaran métodos virtuales y sus herederos los implementan. La clase base proporciona una interfaz común, y las acciones reales dependen del tipo concreto del objeto al que se refiere el puntero o referencia.
Ejemplo de código:
#include <iostream> class Animal { public: virtual void speak() const { std::cout << "Sonido de algún animal "; } virtual ~Animal() {} }; class Dog : public Animal { public: void speak() const override { std::cout << "¡Guau! "; } }; void makeSound(const Animal& a) { a.speak(); } int main() { Dog dog; makeSound(dog); // Salida: ¡Guau! }
Características clave:
virtual en la clase base.override — esto aumenta la seguridad del código.¿Qué sucede si no declaras el destructor de la clase base como virtual?
Eliminar un objeto a través de un puntero a la clase base solo llamará al destructor de la clase base, y el destructor de la clase derivada no será llamado, lo que provocará una fuga de recursos.
Ejemplo de código:
class Base { public: ~Base() { /*...*/ } }; class Derived : public Base { public: ~Derived() { /*...*/ } }; Base* obj = new Derived(); delete obj; // UB: Derived::~Derived no será llamado
¿Se pueden declarar métodos virtuales solo parcialmente y dejar el destructor no virtual?
No, si la clase es polimórfica (tiene al menos una función virtual), el destructor debe ser virtual para evitar fugas de memoria o recursos.
¿Funcionan las funciones virtuales para miembros declarados como static?
No, los miembros estáticos de una clase no pueden ser virtuales, porque no pertenecen a un objeto concreto, y no existe un mecanismo de enlace dinámico para ellos.
override en la clase derivada, lo que lleva a una sobrescritura incorrecta del método.Una jerarquía de clases de dispositivos muy grande, donde cada clase derivada gestiona su propio recurso (por ejemplo, un archivo abierto), pero el destructor de la clase base no es virtual. Al eliminar a través de un puntero a la base, los recursos no se liberan.
Ventajas: El proyecto se construye rápidamente, mínimo de llamadas virtuales.
Desventajas: Fugas de memoria, destrucción incorrecta. Extremadamente difícil de mantener y extender.
Una jerarquía polimórfica bien pensada, donde la clase base tiene funciones virtuales y un destructor virtual. Se utiliza la palabra clave override y los principios de RAII.
Ventajas: Trabajo seguro con recursos, fácil extensión, capacidad de prueba.
Desventajas: Rendimiento ligeramente inferior debido a la búsqueda en vtable, prevención del "over-engineering" si la herencia se aplica sin necesidad.