C/C++ · 11 min read · Oct 10, 2025
Aprendiendo C/C++ Paso a Paso - Página 16
16. Paso a Paso C/C++ — Programación en C++ - Polimorfismo
Polimorfismo
| | 1. Sobrecarga de funciones
- Polimorfismo
- Tipos de polimorfismo
- Funciones miembro normales accedidas con punteros
- Función virtual
- Función pura
- Asignación e Inicialización por Copia
- El Constructor de Copia
- Puntero ‘this’ |
1. Sobrecarga de funciones
Si una función con su nombre difiere por el comportamiento de los argumentos, se llama polimorfismo de funciones o sobrecarga de funciones.
| | // Un programa de ejemplo para demostrar el uso de la sobrecarga de funciones
#include
using namespace std;
void printline()
{
for(int i=0;i<=80; i++) cout << “-“;
}
void printline(int n)
{
for(int i =0 ;i<=n;i++) cout << “-“;
}
void printline(int n,char ch)
{
for(int i=0;i<=n; i++) cout << ch;
}
int main()
{
printline();
printline(5);
printline(10, ‘*’);
return 0;
}
|
Polimorfismo
El polimorfismo es una de las características cruciales de OOP. Simplemente significa un nombre, múltiples formas. Ya hemos visto cómo se implementa el concepto de polimorfismo utilizando funciones y operadores sobrecargados. Las funciones miembro sobrecargadas se seleccionan para invocarse mediante la coincidencia de argumentos, tanto de tipo como de número. El compilador conoce esta información en el tiempo de compilación y, por lo tanto, puede seleccionar la función apropiada para una llamada particular en el mismo tiempo de compilación. Esto se llama vinculación temprana o vinculación estática o enlace estático. También conocido como polimorfismo en tiempo de compilación, vinculación temprana simplemente significa que un objeto está vinculado a su llamada de función en tiempo de compilación.
Ahora consideremos una situación donde el nombre de la función y el prototipo son los mismos en ambas clases base y derivadas. Por ejemplo, considera las siguientes definiciones de clase.
| | #include
using namespace std;
class A
{
int x;
public : void show();
};
class B : public A
{
int y;
public : void show();
};
int main()
{
B b;
b.show();
return 0;
} |
¿Cómo usamos la función miembro show( ) para imprimir los valores de los objetos de ambas clases A y B? Dado que el prototipo de show( ) es el mismo en ambos lugares, la función no está sobrecargada y, por lo tanto, la vinculación estática no se aplica. De hecho, el compilador no sabe qué hacer y pospone la decisión.
Sería bueno si la función miembro apropiada pudiera seleccionarse mientras el programa se está ejecutando. Esto se conoce como polimorfismo en tiempo de ejecución. ¿Cómo podría suceder esto? C++ admite un mecanismo conocido como función virtual para lograr el polimorfismo en tiempo de ejecución. En tiempo de ejecución, cuando se sabe qué objetos de clase están bajo consideración, se llama a la versión apropiada de la función.
Dado que la función se vincula con una clase particular mucho más tarde después de la compilación, este proceso se denomina vinculación tardía. También se conoce como vinculación dinámica o enlace dinámico porque la selección de la función apropiada se realiza dinámicamente en tiempo de ejecución.
3. Tipos de Polimorfismo
El polimorfismo es de dos tipos, a saber.
| 1 | Polimorfismo en tiempo de compilación
O Vinculación temprana
O Vinculación estática
O Polimorfismo de enlace estático. Un objeto está vinculado a su llamada de función en tiempo de compilación. |
| 2 | Polimorfismo en tiempo de ejecución
O vinculación tardía
O Vinculación dinámica
O Polimorfismo de enlace dinámico. La selección y la función apropiada se realiza dinámicamente en tiempo de ejecución. |
| |
|
La vinculación dinámica es una de las características más poderosas de C++. Esto requiere el uso de punteros a objetos. Discutiremos en detalle cómo se utilizan los punteros de objeto y las funciones virtuales para implementar la vinculación dinámica.
4. Funciones Miembro Normales Accedidas con Punteros
El siguiente programa consiste en una clase base
| | / Funciones normales accedidas desde puntero /
/ Polimorfismo con clases (sin usar polimorfismo VIRTUAL /
#include
using namespace std;
class BASE
{
public :
void disp() { cout << “\nEstás en la clase BASE “; }
};
class DERIVED1 : public BASE
{
public :
void disp() { cout << “\nEstás en la clase DERIVED1”; }
};
class DERIVED2 : public BASE
{
public :
void disp() { cout << “\nEstás en la clase DERIVED2”; }
};
int main()
{
DERIVED1 d1; // Objeto de la clase derivada 1
DERIVED2 d2; // Objeto de la clase derivada 2
BASE *b; // puntero a la clase base
b=&d1; // Asignar dirección de d1 en puntero b
b->disp(); // llamada a disp()
b=&d2; // Asignar dirección de d2 puntero b
b->disp(); // llamada a disp()
return 0;
} |
El programa anterior demuestra:
| | • Una clase BASE
• Clases DERIVED1, DERIVED2 derivadas de BASE
• Objetos de clases derivadas (d1,d2)
• Puntero a la clase BASE b | | | *Salida Estás en la clase BASE
Estás en la clase BASE |
5. Función Virtual
Virtual significa existente en efecto pero no en realidad.
Una función miembro puede hacerse como función virtual precediendo la función miembro con la palabra clave virtual.
| | / Polimorfismo con Clases (Polimorfismo Virtual) /
#include
using namespace std;
class B
{
public :
void show(){ cout << “\nclase B método Show() “; }
virtual void disp() { cout << “\nclase B método disp()”; }
};
class D : public B
{
public :
void show(){cout << “\nclase D método Show() “; }
void disp(){ cout << “\nclase D método disp()”; }
};
int main()
{
D d1;
d1.show();
d1.disp(); // Miembro de la clase base
B b;
D d;
B *Bptr;
Bptr = &b;
Bptr->show();
Bptr->disp(); // Miembro de la clase base
Bptr=&d;
Bptr->show(); // miembros de la clase derivada
Bptr->disp(); // Miembro de la clase base
return 0;
} | | | Salida clase D método Show()
clase D método disp() |
6. Función Pura
Una función definida en una clase base y que no tiene definición relativa a la clase derivada se llama función pura. En palabras simples, una función pura es una función virtual sin cuerpo.
| | #include
using namespace std;
class B
{
public :
void show(){ cout << “\nclase B método Show() “; }
virtual void disp() = 0; // función virtual pura
};
class D : public B
{
public :
void show(){cout << “\nclase D método Show() “; }
void disp(){ cout << “\nclase D método disp()”; }
};
int main()
{
D d1;
d1.show(); // O/P : Método de clase D show()
d1.disp(); // O/P : Método de clase D disp()
D d;
B *Bptr;
Bptr=&d;
Bptr->show(); // O/P : Método de clase B show()
Bptr->disp(); // O/P : Método de clase D disp()
return 0;
} |
Bptr -> show() * es la función ejecutable predeterminada de Base Bptr -> disp() es la función ejecutable predeterminada de Base, pero se declara como una función pura virtual, por lo que en tiempo de ejecución se llamará a disp()* de la clase derivada.
| | / Programa para demostrar la ventaja de las funciones virtuales puras /
#include
using namespace std;
enum boolean { false, true };
class NAME
{
protected : char name[20];
public :
void getname()
{ cout << “Ingrese nombre :”; cin >> name; }
void showname()
{ cout << “\nEl nombre es “<< name; }
boolean virtual isGradeA() = 0; // función virtual pura
};
class student : public NAME
{
private : float avg;
public :
void getavg()
{
cout << “\nIngrese Promedio del Estudiante :”;
cin >> avg;
}
boolean isGradeA()
{ return (avg>=80) ? true : false ; }
};
class employee : public NAME
{
private : int sal;
public :
void getsal()
{ cout << “\nIngrese salario “; cin >> sal; }
boolean isGradeA()
{ return (sal>=10000) ? true : false ; }
};
int main()
{
NAME names[20]; // número de punteros a nombre
student s; // puntero a estudiante
employee *e; // puntero a empleado
int n = 0; // número de nombres en la lista
char choice;
do{
cout << “Ingrese Estudiante o Empleado (s/e) “;
cin >> choice;
if(choice==’s’)
{
s = new student; // crear un nuevo estudiante
s->getname();
s->getavg();
names[n++]=s;
}
else
{
e = new employee; // crear un nuevo empleado
e->getname();
e->getsal();
names[n++]=e;
}
cout << “Ingrese otro (y/n) ?”; // hacer otro
cin >> choice;
} while(choice==’y’);
for(int j=0; j
names[j]->showname( );
if(names[j]->isGradeA( )==true)
cout << “Es una persona de Grado 1”;
}
return 0;
} |
7. Asignación e Inicialización por Copia
El compilador de C++ siempre está ocupado en tu nombre, haciendo cosas que no puedes molestarte en hacer. Si tomas el control, se deferirá a tu juicio; de lo contrario, hará las cosas a su manera. Dos ejemplos importantes de este proceso son el operador de asignación y el constructor de copia.
Has utilizado el operador de asignación muchas veces, probablemente sin pensar demasiado en ello. Supongamos que a1 y a2 son objetos. A menos que le digas al compilador lo contrario, la declaración.
a2 = a1; // establece a2 al valor de a1
Causará que el compilador copie los datos de a1, miembro por miembro, en a2. Esta es la acción predeterminada del operador de asignación, =.
También estás familiarizado con la inicialización de variables, inicializando un objeto con otro objeto, como en
alpha a2(a1); // inicializa a2 al valor de a1
Causa una acción similar. El compilador crea un nuevo objeto, a2, y copia los datos de a1, miembro por miembro, en a2. Esta es la acción predeterminada del constructor de copia.
Ambas actividades predeterminadas son proporcionadas, sin costo alguno, por el compilador. Si la copia miembro por miembro es lo que deseas, no necesitas tomar ninguna acción adicional. Sin embargo, si deseas que la asignación de inicialización haga algo más complejo, entonces puedes anular las funciones predeterminadas. Discutiremos las técnicas para sobrecargar el operador de asignación y el constructor de copia por separado.
Sobrecarga del Operador de Asignación
| | // Sobrecarga del Operador de Asignación ( = )
#include
using namespace std;
class alpha
{
private:
int data;
public:
alpha() { } // constructor sin argumentos
alpha( int d )
{ data = d; } // constructor con un argumento
void display()
{ cout << data; } // Mostrar datos
alpha operator =(alpha & a) // operador = sobrecargado
{
data = a.data; // no se hace automáticamente
cout << “\n Operador de asignación invocado “;
return alpha(data);
}
};
int main()
{
alpha a1(37);
alpha a2;
a2 = a1; // Invocar sobrecargado =
cout << “\n a2 = “; a2.display(); // mostrar a2
alpha a3 = a2; // NO invoca =
cout << “\n a3 = “; a3.display(); // mostrar a3
return 0;
} | | | Salida: a2 = 37
a3 = 37 |
8. El CONSTRUCTOR DE COPIA
Como discutimos, podemos definir y al mismo tiempo inicializar un objeto al valor de otro objeto con dos tipos de declaración:
alpha a3(a2); // Inicialización por copia
alpha a3 = a2; // inicialización por copia, sintaxis alternativa
Ambos estilos de definición invocan un constructor de copia: es decir, un constructor que copia su argumento en un nuevo objeto. El constructor de copia predeterminado, que es proporcionado automáticamente por el compilador para cada objeto, realiza una copia miembro por miembro. Esto es similar a lo que hace el operador de asignación; la diferencia es que el constructor de copia también crea un nuevo objeto.
El siguiente ejemplo demuestra el constructor de copia.
| | #include
using namespace std;
class alpha
{
private :
int data;
public:
alpha( ) { } // constructor sin argumentos
alpha(int d) { data = d; } // constructor con un argumento
alpha(alpha& a) // constructor de copia
{
data = a.data;
cout << “\nConstructor de copia invocado”;
}
void display( )
{ cout << data; }
void operator = (alpha& a) // operador = sobrecargado
{
data = a.data;
cout << “\nOperador de asignación invocado”;
}
};
int main()
{
alpha a1( 37 );
alpha a2;
a2 = a1; // invocar sobrecargado =
cout << “ a2 = “; a2.display(); // mostrar a2
alpha a3( a1 ); // invocar constructor de copia
// alpha a3 = a1; // definición equivalente de a3
cout << “ a3 = “; a3.display(); // mostrar a3
return 0;
} |
El programa anterior sobrecarga tanto el operador de asignación como el constructor de copia.
El operador de asignación sobrecargado es similar al del ejemplo anterior.
9. Puntero ‘this’
C++ utiliza una palabra clave única llamada this para representar un objeto que invoca una función miembro. This es un puntero que apunta al objeto para el cual se llamó la función this.
Este puntero simplemente realiza la tarea de devolver el objeto en sí.
El siguiente programa define los objetos i, j y i se asigna con el valor de 5 y todo el objeto de i se asigna a j mediante su función miembro.
| | #include
using namespace std;
class A
{
int a;
public:
A() { }
A(int x)
{ a = x; }
void display()
{
cout << a;
}
A get()
{
return *this; // Devolver a sí mismo
}
};
int main()
{
A i(5);
A j;
j = i.get();
j.display();
return 0;
} |
Ref: Programación orientada a objetos en Turbo C++: Robert Lafore
Recibe nuevas publicaciones en tu bandeja de entrada.
No spam. Cancela la suscripción en cualquier momento.