Ayuda libro de polimorfismo . Solucionado

Iniciado por nolasco281, 3 Marzo 2014, 17:16 PM

0 Miembros y 1 Visitante están viendo este tema.

nolasco281

Hola primero que nada alguno de ustedes sabe de algun libro de C++ que solo hable de polimorfismo y tenga algunos ejemplos un poco mas detallados.

He visto el de obsoluted C++ pero los ejemplos son un poco simples y busco algo que me haga entender a profundidad este tema.

Gracias y saludos a todos.
Lo que se puede imaginar... se puede programar.

eferion

#1
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


    A
    |
 -------
|       |
B       C


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:

Código (cpp) [Seleccionar]

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:

Código (cpp) [Seleccionar]

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:

Código (cpp) [Seleccionar]

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.

Código (cpp) [Seleccionar]

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á:


A
B


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:

Código (cpp) [Seleccionar]

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...

Código (cpp) [Seleccionar]

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:


A
B


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

nolasco281

Hola gracias por responder.

Una pregunta tengo una clase abstracta A y uso funciones virtuales puras, en B y C solo debería de llamar esas funciones y asignarles el código que se necesite no?

Cual es la diferencia de funciones virtuales y vistuales puras?

Como el ejemplo que pones.
Noto que en la clase A describes los métodos virtuales a usar, y en la clase B, que seria la hija, llama a la función virtual solo que con las funcionalidad que necesito (o código que deseo poner, por si no me explico).

Creo que entendí. Pero si estoy mal en algo agradecería la corrección

Gracias de nuevo.


Código (cpp) [Seleccionar]

   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( );
   }


Termino de hacer algunos ejemplos y te comento como me fue.
Lo que se puede imaginar... se puede programar.

eferion

Cita de: nolasco281 en  3 Marzo 2014, 18:16 PM
Cual es la diferencia de funciones virtuales y vistuales puras?

Las funciones virtuales son funciones que pueden ser sobreescritas en las clases hijas. Las funciones virtuales puras son funciones virtuales que no tienen código en la clase padre.

Cuando tú en una clase defines una función virtual pura la clase pasa a ser abstracta, es decir, no puedes crear instancias de esta clase.

Código (cpp) [Seleccionar]


class A
{
  public:
    virtual void func( ) = 0;
};

class B : public A
{
  public:
    void func( )
    { std::cout << "B" << std::endl; }
};

class C : public A
{
  public:
    void otro( )
    { std::cout << "C" << std::endl; }
};
void main( )
{
  A* var1 = new A( ); // Error, A es abstracta
  A* var2 = new B( ); // Correcto
  A* var3 = new C( ); // Error C es abstracta porque no implementa 'func'
}


Cita de: nolasco281 en  3 Marzo 2014, 18:16 PM
Como el ejemplo que pones.
Noto que en la clase A describes los métodos virtuales a usar, y en la clase B, que seria la hija, llama a la función virtual solo que con las funcionalidad que necesito (o código que deseo poner, por si no me explico).

Los métodos virtuales permiten que las clases hijas implementen un código propio sobre una función declarada en el padre, de tal forma que ese código sea llamado siempre independientemente de cual sea "la forma" mediante la cual se acceda a dicho método:

Código (cpp) [Seleccionar]

class A
    {
     public:
       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( );
     B* ptr3 = dynamic_cast< B*>( ptr2 );

     ptr1->func1( );
     ptr2->func1( );
     ptr3->func1( );
    }


Fíjate que he quitado el modificador de "virtual" en la clase padre. En este caso la salida del programa será:


A
A
B


Esto es así porque, al no ser virtual, el resultado de func1( ) dependerá del tipo de puntero ( A o B ) y no del tipo de la instancia a la que apunta ( A o B ).

Como corolario, en C++11 se añaden dos modificadores adicionales para estos casos; uno de ellos es especialmente útil. Se trata del modificador "override".

"override" se usa en las funciones virtuales que sobreescribes en las clases hijas y su función es obligar al compilador a que se asegure de que realmente la función esta sobreescribiendo una función virtual... si no es así resulta un error de compilación.

Código (cpp) [Seleccionar]

class A
{
  public:
    virtual void func( )
    { }

    virtual void funcConNombreRarillo( )
    { }
};

class B : public A
{
  public:
    // ok
    voif func( ) override;

    // Error: no hay ninguna func( int ) declarada virtual en el padre
    void func( int param ) override;

    // Error: Se me ha colado una l de mas en el nombre
    void funcConNombreRarilllo( ) override;
};


El modificador "override" es bastante útil porque, de otra forma" no tenemos posibilidad de detectar estos errores a la hora de programar. Es muy sencillo equivocarse en la firma de la función y es ciertamente complicado detectar el problema.


nolasco281

Muchas gracias eferion siempre sancando la dudas de ensima.

lamento responder tan tarde no tenia internet. saludos.
Lo que se puede imaginar... se puede programar.

eferion

Sin problemas.

Tu pregunta que seguro que alguien sabe la respuesta ;)

dato000

#6
Vaya genial explicación, habia visto esa parte para c#, pero no para c++, virtuales yeah!!!!!!!

dejo una pequeña modificación del código porque no corria correctamente, solo detalles.


Código (cpp) [Seleccionar]
#include "iostream"
using namespace std;

class A
{
   public:
       virtual void func() {}; // se supone que es abstracta
};

class B : public A
{
   public:
           void func()
           {
               cout << "Resultado B" << endl;
           }
};

class C : public A
{
   public:
       void otro()
       {
           cout << "Resultado C" << endl;
       }
} ;

int main()
{
   //A* var1 = new A(); // No puede instanciar directamente una clase virtual
   A* var2 = new B(); // Correcto, usa la instancia de clase A
   //A* var3 = new C(); // clase C no usa instancia de A

   var2 -> func();

   return 0;
}





eferion

Cita de: dato000 en  6 Marzo 2014, 16:55 PM
dejo una pequeña modificación del código porque no corria correctamente, solo detalles.
Esa era justamente la idea... no corre porque al ser virtual pura no puedes instanciar clases de ese tipo.

Lo que has conseguido con ese cambio es que 'A' no sea abstracta y, por tanto, puede ser instanciable.

Cita de: dato000 en  6 Marzo 2014, 16:55 PM
Vaya genial explicación, habia visto esa parte para c#, pero no para c++, virtuales yeah!!!!!!!

Celebro que te haya gustado.

Para más dudas aquí estamos :)