✅ Objetivo: Comprender y aplicar polimorfismo en tiempo de ejecución, destructores virtuales y herencia múltiple ✅ Nivel: Intermedio-Avanzado ✅ Temas clave: clases base, métodos virtuales, constructores en herencia, slicing, comportamiento dinámico
- Clases Base y Derivadas
- Métodos Virtuales y Overriding
- Destructores Virtuales
- Clases Abstractas y Métodos Puros
- Herencia Múltiple
- Object Slicing
- Errores Comunes
- Buenas Prácticas
📘 ¿Qué es? Una clase derivada hereda atributos y métodos de otra clase base.
class Animal {
public:
void respirar() const {
std::cout << "Respirando..." << std::endl;
}
};
class Perro : public Animal {
public:
void ladrar() const {
std::cout << "Guau!" << std::endl;
}
};🎯 Se utiliza para modelar relaciones tipo "es-un".
📘 ¿Qué es? Un método virtual se puede redefinir en una clase derivada y ejecutará el método correcto en tiempo de ejecución.
class Animal {
public:
virtual void hablar() const {
std::cout << "Hace un sonido" << std::endl;
}
};
class Gato : public Animal {
public:
void hablar() const override {
std::cout << "Miau!" << std::endl;
}
};🧪 Uso:
Animal* a = new Gato();
a->hablar(); // Llama a Gato::hablar() gracias al virtual🎯 Sin virtual, llamaría a Animal::hablar() aunque sea un Gato.
📘 ¿Por qué importa? Para evitar fugas de memoria al eliminar objetos derivados desde punteros base.
class Base {
public:
virtual ~Base() {
std::cout << "Destruyendo Base" << std::endl;
}
};
class Derivada : public Base {
public:
~Derivada() {
std::cout << "Destruyendo Derivada" << std::endl;
}
};🧪 Uso correcto:
Base* obj = new Derivada();
delete obj; // Llama a ~Derivada y luego a ~Base🎯 Siempre haz el destructor base virtual si hay herencia.
📘 ¿Qué es? Una clase abstracta tiene al menos un método puro virtual y no se puede instanciar.
class Forma {
public:
virtual void dibujar() const = 0; // Método puro
};🎯 Las subclases deben implementar dibujar().
class Circulo : public Forma {
public:
void dibujar() const override {
std::cout << "Dibujo un círculo" << std::endl;
}
};🧪 Uso:
Forma* f = new Circulo();
f->dibujar(); // Dibujo un círculo📘 ¿Qué es? Una clase puede heredar de más de una base.
class Volador {
public:
void volar() const {
std::cout << "Volando..." << std::endl;
}
};
class Cantante {
public:
void cantar() const {
std::cout << "Cantando!" << std::endl;
}
};
class Pajaro : public Volador, public Cantante {};🧪 Uso:
Pajaro p;
p.volar();
p.cantar();🎯 Ten cuidado con ambigüedad de nombres si dos clases base tienen el mismo método.
📘 ¿Qué es? Cuando asignas una clase derivada a una variable de clase base por valor, se pierde la parte derivada.
class A {
public:
virtual void foo() const {
std::cout << "A::foo" << std::endl;
}
};
class B : public A {
public:
void foo() const override {
std::cout << "B::foo" << std::endl;
}
};
A a = B(); // slicing: B → A
a.foo(); // A::foo, NO B::foo🎯 Solución: siempre usa punteros o referencias para objetos polimórficos.
❌ Olvidar virtual en métodos y destructores
❌ Object slicing: pasar clases por valor
❌ Intentar instanciar una clase abstracta
❌ Herencia múltiple con ambigüedad no resuelta
❌ No usar override, lo que puede ocultar errores sutiles
✅ Marca destructores como virtual en clases base
✅ Usa override para métodos redefinidos
✅ Evita herencia múltiple salvo cuando sea realmente necesario
✅ Usa punteros/referencias para evitar slicing
✅ Marca clases como abstractas si solo sirven de interfaz
Este proyecto está bajo la licencia MIT. ⚖️📄🆓