10. Pilares de POO: Herencia y Polimorfismo Dinámico 🏛️⚡
Descubre los superpoderes arquitectónicos de C++: cómo videojuegos como World of Warcraft o Cyberpunk gestionan cientos de entidades distintas con una interfaz común.
La herencia permite crear una clase derivada (hija) que absorbe todos los atributos y métodos de una clase base (padre), añadiendo sus propias especializaciones.
herencia_personajes.cpp
CPP
1#include <iostream>
2#include <string>
3
4// Clase Base
5class Personaje {
6protected:
7std::string nombre;
8int salud;
9
10public:
11Personaje(std::string n, int s) : nombre(n), salud(s) {}
12
13voidrecibirDanio(int danio) {
14 salud -= danio;
15std::cout << nombre << " recibió " << danio << " de daño. Salud restante: " << salud << "\n";
16 }
17};
18
19// Clase Derivada (Hereda de Personaje)
20class Mago : public Personaje {
21private:
22int mana;
23
24public:
25Mago(std::string n, int s, int m) : Personaje(n, s), mana(m) {}
26
27voidlanzarHechizo() {
28std::cout << nombre << " lanza Bola de Fuego consumiendo 20 de mana!\n";
29 mana -= 20;
30 }
31};
💡 ¿Por qué usamos protected?
Si los atributos fueran private en Personaje, ni siquiera la clase hija Mago podría acceder a nombre. Con protected, quedan protegidos del mundo exterior pero accesibles para sus clases hijas.
2. Polimorfismo Dinámico: La Magia de virtual y override
El polimorfismo te permite tratar a objetos diferentes a través de un mismo puntero a la clase base, ejecutando la versión especializada de cada uno en tiempo de ejecución.
🔮 ¿Por qué es obligatoria la palabra clave virtual?
Por defecto, C++ enlaza las llamadas a métodos en tiempo de compilación según el tipo del puntero. Al marcar una función como virtual, C++ activa la vtable (tabla de métodos virtuales), averiguando el tipo real del objeto en tiempo de ejecución.
polimorfismo_videojuego.cpp
CPP
1#include <iostream>
2#include <vector>
3
4class Enemigo {
5public:
6virtualvoidatacar() const {
7std::cout << "Ataque genérico del enemigo.\n";
8 }
9// Destructor virtual obligatorio para liberar memoria limpia:
23std::cout << "El Zombie muerde al jugador... 🧟\n";
24 }
25};
26
27intmain() {
28// Vector polimórfico de punteros a la base:
29std::vector<Enemigo*> horda;
30 horda.push_back(newDragon());
31 horda.push_back(newZombie());
32
33// El bucle principal no sabe qué criatura es, solo sabe que puede atacar:
34for (Enemigo* e : horda) {
35 e->atacar(); // Ejecuta el ataque correcto de cada criatura
36 }
37
38// Limpieza de memoria:
39for (Enemigo* e : horda) delete e;
40return0;
41}
3. ⚠️ El Destructor Virtual: Previniendo Fugas de Memoria
Si eliminas un objeto derivado a través de un puntero a la clase base (delete e;), y la clase base no tiene un destructor virtual, ¡solo se ejecutará el destructor de la base y la memoria de la clase hija quedará filtrada en la RAM!
virtual ~ClaseBase() = default; // Agrega esto siempre a tus clases base
Ejercicio de Cierre del Curso
Diseña una clase base abstracta Figura con un método virtual puro virtual double calcularArea() const = 0;. Implementa dos clases derivadas: Rectangulo (ancho, alto) y Circulo (radio). Crea un vector de punteros a Figura y muestra el área de cada figura en un bucle polimórfico.