El polimorfismo es un concepto de lo más sencillo... bien es cierto que yo en la universidad veía a gente temblando solo de pensar en ello... pero yo creo que es más por el nombre que por otra cosa.
Tú tienes una jerarquía de clases, por ejemplo
Donde A es la clase padre y B y C las hijas.
Si tú creas una clase B o una clase C ( es indiferente ) puedes gestionarlas como si fuesen directamente del tipo A:
std::vector< A* > lista;
lista.push_back( new B( ) );
lista.push_back( new C( ) );
Los cast a los padres son automáticos, no hace falta hacer un dynamic_cast... sin embargo moverte hacia abajo en la herencia sí requiere un dynamic_cast por motivos obvios... C es de tipo A, pero no es de tipo B:
A* clase = new C( );
C* c = dynamic_cast< C* >( clase ); // puntero valido
B* b = dynamic_cast< B* >( clase ); // puntero nulo, no es posible el cast
También es lógico pensar que si haces un cast al padre, los métodos propios de las clases hijas no son accesibles... no hasta que vuelvas a hacer un cast a la clase hija:
class A
{
public:
void func1( );
};
class B : public A
{
public:
void func2( );
};
void main( )
{
A* puntero = new B( );
puntero->func1( ); // ok
puntero->func2( ); // error, A no tiene un miembro func2
B* puntero2 = dynamic_cast< B* >( puntero );
puntero2->func1( ); // ok
puntero2->func2( ); // ok
}
La excepción a esta última norma son los métodos virtuales. Si tu sobrecargas un método virtual de una clase padre, se llamará al método de la clase hija SIEMPRE.
class A
{
public:
virtual void func1( )
{ std::cout << "A" << std::endl; }
};
class B : public A
{
public:
void func1( )
{ std::cout << "B" << std::endl; }
};
void main( )
{
A* ptr1 = new A( );
A* ptr2 = new B( );
ptr1->func1( );
ptr2->func1( );
}
La salida del programa será:
Un aspecto importante del polimorfismo es que al moverse en la jerarquía los punteros no tienen por qué coincidir... esto se hace muy patente con herencia múltiple:
class A
{
int dummy;
};
class B
{
int dummy;
};
class C : public A, public B
{
int dummy;
};
void main( )
{
C* puntero1 = new C( );
B* puntero2 = puntero1;
A* puntero3 = puntero1;
std::cout << "Puntero clase C: " << static_cast< void* >( puntero1 ) << std::endl;
std::cout << "Puntero clase B: " << static_cast< void* >( puntero2 ) << std::endl;
std::cout << "Puntero clase A: " << static_cast< void* >( puntero3 ) << std::endl;
}
Este comportamiento es debido a que C++ introduce unos identificadores en las clases para poder navegar por la jerarquía ( estos identificadores son los que usa dynamic_cast para saber si un cast es válido o no )
Y poco más que contar... ah sí, se me olvidaba... si en vez de puntero trabajas con la pila has de tener en cuenta que si haces un cast al padre vas a perder la herencia, ya que estarás llamando al constructor copia...
class A
{
public:
virtual void func1( )
{ std::cout << "A" << std::endl; }
};
class B : public A
{
public:
void func1( )
{ std::cout << "B" << std::endl; }
};
void main( )
{
A clase1 = B( );
B clase2 = B( );
clase1.func1( );
clase2.func1( );
}
Salida:
Y esto es el polimorfismo... la gracia de esto es que es extensible... yo he puesto ejemplos con 3 clases... pero en el programa en el que trabajo el polimorfismo abarca unas 300 clases repartidas por una decena de niveles de jerarquía con herencias múltiples y demás guarrerías XD