POLIMORFISMO
La palabra "polimorfismo" significa "la facultad de asumir muchas formas", refiriéndose a la facultad de llamar a muchas funciones diferentes en una sola sentencia.
Funciones Virtuales
Una función virtual es un método de una clase base que puede ser redefinida en cada una de las clases derivadas, podrá utilizar los métodos redefinidos en esta clase derivada.
Una función virtual se define color cuando la palabra "virtual" antes de la declaración del método en la clase base.
Ejemplo:
// Punteros a clases derivadas #include<iostream.h> #include<conio.h> class A { int a; public: void DefineA(int); int DameA(void); virtual void mostrar(void); }; void A::DefineA(int x) { a= x; } int A::DameA(void) { return a; } void A::mostrar(void) { cout << "Clase A" << endl; cout << "a = " << a << endl; } class B : public A { int b; public: void DefineB(int); void mostrar(void); void mostrarA(void); }; void B::DefineB(int x) { b= x; } void B::mostrar(void) { cout << "Clase B" << endl; cout << "b = " << b << endl; } void B::mostrarA(void) { cout << "Clase B" << endl; cout << "a = " << DameA() << endl; } void main (void) { A *pa; B *pb; clrscr(); pb = new B; pb->DefineA(10); pb->DefineB(5); cout << "Desde pb :" << endl; pb->mostrar(); // 1ra. Parte: pa apunta a objeto de clase B pa = pb; pa->DefineA(25); pa->DefineB(25); INVALIDO DefineB no es un método // de la clase A cout << "pa apunta a objeto de clase B:" << endl; pa->mostrar(); //ejecuta el método definido en B // 2da. Parte : pb apunta a un objeto de clase A pa = new A; pa->DefineA(35); cout << "Dese pa :" << endl; pa->mostrar(); //ejecuta el método definido en A // pb = pa; INVALIDO pb = (B*)pa; pb->DefineB(82); cout << "pb apunta a objeto de clase A:" << endl; pb->mostrarA(); pb->mostrar(); //ejecuta el método definido en A pb->DefineA(56); pb->mostrarA(); } /* Al ejecutar el programa se obtiene: Desde pb : Clase B b = 5 pa apunta a objeto de clase B: Clase B b = 5 Desde pa : Clase A a = 35 pb apunta a objeto de clase A: Clase B a = 35 Clase A a = 35 Clase B a = 56 */
Observe el siguiente programa:
#include<iostream.h> class A { public: virtual void Imprime(void); }; void A::Imprime(void) { cout << "Estoy en A" << endl; } class B: public A { public: void Imprime(void); }; void B::Imprime(void) { cout << "Estoy en B" << endl; } class C: public A { public: void Imprime(void); }; void C::Imprime(void) { cout << "Estoy en C" << endl; } class D: public A { public: void Imprime(void); }; void D::Imprime(void) { cout << "Estoy en D" << endl; } class DD: public D { public: virtual void Imprime(void); }; void DD::Imprime(void) { cout << "Estoy en DD" << endl; } // Prototipos: void Funcion1(void); void Funcion2(void); void MuestraObjeto(A *); void main(void) { Funcion1(); Funcion2(); } void Funcion1(void) { A *pa[5], *aux; cout<<"Ejecución de la función 1: "<<endl; pa[0]= new A; pa[1]= new B; pa[2]= new C; pa[3]= new D; pa[4]= new DD; for (int i=0; i<5; i++) pa[i]->Imprime(); } void Funcion2(void) { A Oa; B Ob; C Oc; D Od; DD Odd; cout<< endl <<"Ejecución de la función 2: "<< endl; MuestraObjeto(&Oa); MuestraObjeto(&Ob); MuestraObjeto(&Oc); MuestraObjeto(&Od); MuestraObjeto(&Odd); } void MuestraObjeto(A *O) { O->Imprime(); } /* Al ejecutar el programa se obtiene : Ejecución de la función 1: Estoy en A Estoy en B Estoy en C Estoy en D Estoy en DD Ejecución de la función 2: Estoy en A Estoy en B Estoy en C Estoy en D Estoy en DD */
Se puede apreciar que todos los enlaces se hicieron en tiempo de ejecución.
Además se puede apreciar que una función virtual en una clase base continúa siéndolo cuando es heredada.
Implementación de las Funciones Virtuales
Al definir una función virtual, el compilador no podrá identificar la función que va a ser llamada por el puntero, ya que puede ser cualquiera de varias funciones diferentes. Por lo tanto, el compilador debe añadir código que permita evaluar la sentencia en tiempo de ejecución, que es cuando recién se puede conocer el tipo de objeto al que apuntará y por consiguiente saber a qué función hay que invocar.
Esto se conoce como "ligadura dinámica" o "ligadura retrasada".
Esto es una implementación muy diferente al de las funciones clásicas de C y C++ ya que la llamada a la función en estos casos, es convertida durante la compilación en un salto a una dirección fija que coincida con el inicio del código de la función. Esto último se denomina "ligadura estática".
La ligadura en C++ se implementa a través de una tabla de funciones virtuales llamada "v-table". Esta tabla está formada por un arreglo de puntero a funciones.
En el último ejemplo, la implementación llevó a un esquema similar al siguiente:
Cuando se crea la variable referenciada por un puntero a un objeto con funciones virtuales, esta variable contendrá un puntero oculto que apuntará a una entrada a v-table dependiendo del tipo de variable referenciada, esto es:
A *pa; pa = new B;
En estos momentos se define un objeto bajo el siguiente esquema:
Si luego se hace:
Delete pa; pa = new C
Se obtiene:
Funciones Virtuales Puras
Son funciones virtuales que no tienen cuerpo o implementación y que tampoco se pueden ejecutar.
Sintaxis:
virtual tipo nombre (parámetros) = 0;
Ejemplo:
virtual tipo Imprime (void) = 0;
Las funciones virtuales puras nos van a permitir darle la calidad de "virtual" a funciones declaradas en clases derivadas sin tener que definir instrucciones que no vienen al caso en la clase Base. Dicho en otras palabras, las funciones virtuales puras dejan para más adelante (cuando se defina la clase derivada) su implementación:
#include<string.h> #include<iostream.h> #include<conio.h> #include<iomanip.h> class Persona { char Nombre[40]; char Direccion[40]; char FechaNac[11]; public: Persona(char *, char *, char *); virtual void MuestraDatos(void)=0; char *DameNombre(void); char *DameDireccion(void); char *DameFechaNac(void); }; Persona::Persona(char *N, char *D, char *F) { strcpy(Nombre, N); strcpy(Direccion, D); strcpy(FechaNac, F); } char *Persona::DameNombre(void) { return Nombre; } char *Persona::DameDireccion(void) { return Direccion; } char *Persona::DameFechaNac(void) { return FechaNac; } class Profesor : public Persona { char Categoria[30]; char Dedicacion[5]; public : Profesor(char *, char *, char *, char *, char *); void MuestraDatos(void); char *DameCategoria(void); char *DameDedicacion(void); }; Profesor::Profesor(char *N, char *D, char *F, char *C, char *De): Persona(N, D, F) { strcpy(Categoria, C); strcpy(Dedicacion, De); } void Profesor::MuestraDatos(void) { cout << "Datos del Profesor: " << endl; cout << "Nombre: " << DameNombre() << endl; cout << "Categoria: " << DameCategoria() << endl; cout << "Dedicación: " << DameDedicacion() << endl; } char *Profesor::DameCategoria(void) { return Categoria; } char *Profesor::DameDedicacion(void) { return Dedicacion; } class Alumno: public Persona { char Especialidad[40]; int NumeroDeCreditos; public: Alumno(char *, char *, char *, char *, int); void MuestraDatos(void); char *DameEspecialidad(void); int DameNumeroDeCreditos(void); }; Alumno::Alumno(char *N, char *D, char *F, char *E, int C): Persona(N, D, F) { strcpy(Especialidad, E); NumeroDeCreditos = C; } void Alumno::MuestraDatos(void) { cout << "Datos del Alumno: " << endl; cout << "Nombre: " << DameNombre() << endl; cout << "Especialidad: " << DameEspecialidad() << endl; cout << "Número de Créditos: " << DameNumeroDeCreditos() << endl; } char *Alumno::DameEspecialidad(void) { return Especialidad; } int Alumno::DameNumeroDeCreditos(void) { return NumeroDeCreditos; } class Empleado: public Persona { char Departamento[40]; char Cargo[40]; public: Empleado(char *, char *, char *, char *, char *); void MuestraDatos(void); char *DameDepartamento(void); char *DameCargo(void); }; Empleado::Empleado(char *N, char *D, char *F, char *De, char *C): Persona(N, D, F) { strcpy(Departamento, De); strcpy(Cargo, C); } void Empleado::MuestraDatos(void) { cout << "Datos del Empleado: " << endl; cout << "Nombre: " << DameNombre() << endl; cout << "Departamento: " << DameDepartamento() << endl; cout << "Cargo: " << DameCargo() << endl; } char *Empleado::DameDepartamento(void) { return Departamento; } char *Empleado::DameCargo(void) { return Cargo; } // Prototipos: void Imprime(Persona *); void main(void) { Profesor P("Mario Pérez", "Av. ABC 123", "14/03/70" , "Auxiliar","TC") ; Alumno A("Sofía Guzmán", "Av. XYZ","14/09/80", "Ing. Informática",120); Empleado E("Ana Ruiz", "Jr. PQR 555","07/12/72","RR. HH.", "Secretaria"); Imprime(&P); cout << endl; Imprime(&A); cout << endl; Imprime(&E); } void Imprime(Persona *Objeto) { Objeto->MuestraDatos(); } /* Al ejecutar el programa se obtiene: Datos del Profesor: Nombre: Mario Pérez Categoria: Auxiliar Dedicación: TC Datos del Alumno: Nombre: Sofía Guzmán Especialidad: Ing. Informática Número de Créditos: 120 Datos del Empleado: Nombre: Ana Ruiz Departamento: RR. HH. Cargo: Secretaria */
Clases Abstractas
Una clase abstracta es una clase que puede utilizarse sólo como clase base de otras clases. Esto significa que no se podrá definir un objeto de esta clase base, sin embargo, si se podrá definir objetos de sus clases derivadas.
Una clase, para ser definida como abstracta, debe tener al menos una función virtual pura.
Una clase abstracta se puede emplear para definir punteros que, a través del programa, apuntarán a objetos de sus clases derivadas, más nunca a un objeto de esa clase.
Para que en un programa se produzca el polimorfismo se tiene que seguir los siguientes pasos:
Crear una jerarquía de clases en la que operaciones importantes serán declaradas como métodos virtuales, puros o no.
![]() |
![]() ![]() |