Polimorfismo en C++ explicado: Ejemplos prácticos y conceptos clave
¿Qué es el polimorfismo en C++?
En C++, el polimorfismo hace que una función miembro se comporte de manera diferente según el objeto que la llama/invoca. Polimorfismo es una palabra griega que significa "tener muchas formas". Ocurre cuando tiene una jerarquía de clases relacionadas mediante herencia.
Por ejemplo, supongamos que tenemos la función hacerSonido(). Cuando un gato llama a esta función, producirá un maullido. Cuando una vaca invoca la misma función, producirá un mugido.

Aunque tenemos una sola función, se comporta diferente bajo distintas circunstancias. La función tiene muchas formas; por tanto, hemos logrado polimorfismo.
En este tutorial de C++ aprenderá:
- Qué es el polimorfismo
- Tipos de polimorfismo
- Polimorfismo en tiempo de compilación
- Sobrecarga de funciones
- Sobrecarga de operadores
- Polimorfismo en tiempo de ejecución
- Sobrescritura de funciones
- Función virtual en C++
- Polimorfismo en tiempo de compilación vs. tiempo de ejecución
Tipos de polimorfismo
C++ soporta dos tipos de polimorfismo:
- Polimorfismo en tiempo de compilación, y
- Polimorfismo en tiempo de ejecución.

Polimorfismo en tiempo de compilación
Invoca las funciones sobrecargadas coincidiendo con el número y tipo de argumentos. La información está presente durante la compilación. Esto significa que el compilador de C++ seleccionará la función correcta en tiempo de compilación.
El polimorfismo en tiempo de compilación se logra mediante sobrecarga de funciones y sobrecarga de operadores.
Sobrecarga de funciones
La sobrecarga de funciones ocurre cuando tenemos muchas funciones con nombres similares pero argumentos diferentes. Los argumentos pueden diferir en número o tipo.
Ejemplo 1:
#include <iostream>
using namespace std;
void test(int i) {
cout << "El int es " << i << endl;
}
void test(double f) {
cout << "El float es " << f << endl;
}
void test(char const *ch) {
cout << "El char* es " << ch << endl;
}
int main() {
test(5);
test(5.5);
test("cinco");
return 0;
}
Salida:

Captura del código:

Explicación del código:
- Incluya cabecera iostream para usar sus funciones.
- Incluya espacio de nombres std para usar sus clases sin llamarlas.
- Cree función test que tome parámetro entero i. { marca inicio del cuerpo.
- Sentencia a ejecutar si se invoca la función test anterior.
- Fin del cuerpo de la función test anterior.
- Cree función test que tome parámetro float f. { marca inicio del cuerpo.
- Sentencia a ejecutar si se invoca la función test anterior.
- Fin del cuerpo de la función test anterior.
- Cree función test que tome parámetro carácter ch. { marca inicio del cuerpo.
- Sentencia a ejecutar si se invoca la función test anterior.
- Fin del cuerpo de la función test anterior.
- Llame a main(). { marca inicio del cuerpo.
- Llame a test pasando 5. Invoca test que acepta entero (primera función).
- Llame a test pasando 5.5. Invoca test que acepta float (segunda función).
- Llame a test pasando "cinco". Invoca test que acepta carácter (tercera función).
- El programa debe retornar valor si se ejecuta exitosamente.
- Fin del cuerpo de main().
Tenemos tres funciones con el mismo nombre pero diferentes tipos de argumentos. Hemos logrado polimorfismo.
Sobrecarga de operadores
En la sobrecarga de operadores, definimos un nuevo significado para un operador de C++. También cambia cómo funciona el operador. Por ejemplo, podemos definir el operador + para concatenar dos cadenas. Lo conocemos como operador de adición para sumar valores numéricos. Tras nuestra definición, cuando se coloca entre enteros, los sumará. Cuando se coloca entre cadenas, las concatenará.
Ejemplo 2:
#include<iostream>
using namespace std;
class NumeroComplejo {
private:
int real, imag;
public:
NumeroComplejo(int rl = 0, int im = 0) {
real = rl;
imag = im;
}
NumeroComplejo operator + (NumeroComplejo const &obj) {
NumeroComplejo resultado;
resultado.real = real + obj.real;
resultado.imag = imag + obj.imag;
return resultado;
}
void imprimir() {
cout << real << " + i" << imag << endl;
}
};
int main() {
NumeroComplejo c1(10, 2), c2(3, 7);
NumeroComplejo c3 = c1 + c2;
c3.imprimir();
}
Salida:

Captura del código:


Explicación del código:
- Incluya iostream para usar sus funciones.
- Incluya espacio de nombres std.
- Cree clase NumeroComplejo. { marca inicio del cuerpo.
- Use modificador private para marcar variables como privadas (solo accesibles dentro de la clase).
- Defina dos variables enteras: real e imag.
- Use public para marcar constructor como público (accesible desde fuera).
- Cree constructor inicializando variables.
- Inicialice valor de real.
- Inicialice valor de imag.
- Fin del cuerpo del constructor.
- Necesitamos sobrescribir el significado del operador +.
- Cree tipo de dato resultado de tipo NumeroComplejo.
- Use operador + con números complejos. Esta línea suma la parte real de un número a la parte real de otro.
- Use operador + con números complejos. Esta línea suma la parte imaginaria de un número a la parte imaginaria de otro.
- El programa retornará el valor de resultado al completarse exitosamente.
- Fin de la definición del nuevo significado de + (sobrecarga).
- Llame al método imprimir().
- Imprima el nuevo número complejo tras la adición en consola.
- Fin del cuerpo de imprimir().
- Fin del cuerpo de la clase NumeroComplejo.
- Llame a main().
- Pase valores de partes real e imaginaria a sumar. Primera parte de c1 se suma a primera de c2 (10+3). Segunda parte de c1 se suma a segunda de c2 (2+7).
- Realice operación usando operador + sobrecargado y guarde resultado en c3.
- Imprima valor de c3 en consola.
- Fin del cuerpo de main().
Polimorfismo en tiempo de ejecución
Esto ocurre cuando el método de un objeto es invocado/llamado durante la ejecución en lugar de durante la compilación. El polimorfismo en tiempo de ejecución se logra mediante sobrescritura de funciones. La función a llamar se establece durante la ejecución.
Sobrescritura de funciones
La sobrescritura de funciones ocurre cuando una función de la clase base recibe una nueva definición en una clase derivada. En ese momento, decimos que la función base ha sido sobrescrita.
Por ejemplo:
#include <iostream>
using namespace std;
class Mamifero {
public:
void comer() {
cout << "Los mamíferos comen...";
}
};
class Vaca : public Mamifero {
public:
void comer() {
cout << "Las vacas comen hierba...";
}
};
int main(void) {
Vaca v = Vaca();
v.comer();
return 0;
}
Salida:

Captura del código:

Explicación del código:
- Importe iostream para usar sus funciones.
- Incluya espacio de nombres std.
- Cree clase Mamifero. { marca inicio del cuerpo.
- Use public para marcar la función que crearemos como públicamente accesible.
- Cree función pública comer(). { marca inicio del cuerpo.
- Imprima la sentencia añadida a cout cuando comer() se invoque.
- Fin del cuerpo de comer().
- Fin del cuerpo de la clase Mamifero.
- Cree clase Vaca que hereda de Mamifero. Vaca es clase derivada, Mamifero es base. { marca inicio.
- Use public para marcar función como públicamente accesible.
- Sobrescriba la función comer() definida en la clase base. { marca inicio del cuerpo.
- Sentencia a imprimir en consola cuando esta función se invoque.
- Fin del cuerpo de comer().
- Fin del cuerpo de la clase Vaca.
- Llame a main(). { marca inicio del cuerpo.
- Cree instancia de Vaca dándole nombre v.
- Llame a comer() definido en Vaca.
- El programa debe retornar valor al completarse exitosamente.
- Fin de main().
Función virtual en C++
Una función virtual es otra forma de implementar polimorfismo en tiempo de ejecución en C++. Es una función especial definida en una clase base y redefinida en la clase derivada. Para declarar una función virtual, use la palabra clave virtual. La palabra clave debe preceder la declaración de la función en la clase base.
Si una clase virtual es heredada, la clase derivada redefine la función virtual para adaptarla a sus necesidades. Por ejemplo:
#include <iostream>
using namespace std;
class ClaseA {
public:
virtual void mostrar() {
cout << "La función mostrar() en clase base invocada..." << endl;
}
};
class ClaseB :public ClaseA {
public:
void mostrar() {
cout << "La función mostrar() en clase derivada invocada...";
}
};
int main() {
ClaseA* a;
ClaseB b;
a = &b;
a->mostrar();
}
Salida:

Captura del código:

Explicación del código:
- Incluya iostream en el código para usar sus funciones.
- Incluya espacio de nombres std.
- Cree clase ClaseA.
- Use public para marcar miembro como públicamente accesible.
- Cree función virtual mostrar(). Será pública.
- Texto a imprimir cuando mostrar() se invoque. endl mueve cursor a siguiente línea.
- Fin del cuerpo de la función virtual mostrar().
- Fin del cuerpo de ClaseA.
- Cree nueva clase ClaseB que hereda ClaseA. ClaseA es base, ClaseB es derivada.
- Use public para marcar miembro como públicamente accesible.
- Redefina la función virtual mostrar() definida en la base.
- Texto a imprimir cuando mostrar() definido en derivada se invoque.
- Fin del cuerpo de mostrar().
- Fin del cuerpo de la clase derivada ClaseB.
- Llame a main(). La lógica del programa va en su cuerpo.
- Cree variable puntero a. Apunta a ClaseA.
- Cree instancia de ClaseB llamada b.
- Asigne la dirección almacenada en b a la variable a.
- Invoque la función mostrar() definida en la clase derivada. Se ha implementado enlace tardío (late binding).
- Fin del cuerpo de main().
Polimorfismo en tiempo de compilación vs. tiempo de ejecución
Aquí están las principales diferencias:
| Polimorfismo en tiempo de compilación | Polimorfismo en tiempo de ejecución |
|---|---|
| También llamado enlace temprano (early binding) o polimorfismo estático | También llamado enlace tardío (late/dynamic binding) o polimorfismo dinámico |
| El método se llama/invoca durante la compilación | El método se llama/invoca durante la ejecución |
| Implementado vía sobrecarga de funciones y operadores | Implementado vía sobrescritura de métodos y funciones virtuales |
| Ejemplo: sobrecarga de métodos. Muchos métodos con nombres similares pero diferente número o tipos de argumentos | Ejemplo: sobrescritura de métodos. Muchos métodos con nombre similar y mismo prototipo |
| Ejecución más rápida ya que el descubrimiento de métodos ocurre en compilación | Ejecución más lenta ya que el descubrimiento ocurre en tiempo de ejecución |
| Menos flexibilidad para resolver problemas ya que todo se conoce en compilación | Mucha flexibilidad para resolver problemas complejos ya que métodos se descubren en ejecución |
Resumen:
- Polimorfismo significa tener muchas formas.
- Ocurre cuando hay una jerarquía de clases relacionadas mediante herencia.
- Con polimorfismo, una función puede comportarse diferente según el objeto que la invoca/llama.
- En polimorfismo en tiempo de compilación, la función a invocar se establece durante la compilación.
- En polimorfismo en tiempo de ejecución, la función a invocar se establece durante la ejecución.
- El polimorfismo en tiempo de compilación se determina mediante sobrecarga de funciones y operadores.
- En sobrecarga de funciones, hay muchas funciones con nombres similares pero argumentos diferentes.
- Los parámetros pueden diferir en número o tipo.
- En sobrecarga de operadores, se define un nuevo significado para operadores de C++.
- El polimorfismo en tiempo de ejecución se logra mediante sobrescritura de funciones.
- En sobrescritura, una clase derivada da nueva definición a una función definida en la clase base.
Lenguaje C
- Cómo Pasar Arrays a Funciones en C++: Guía Práctica
- Operadores en C++ explicados: Tipos, ejemplos y programas de muestra
- Dominando switch-case en C++: Sintaxis, uso y ejemplos prácticos
- Asignación dinámica de arrays en C++: Guía práctica con ejemplos de código
- Sobrecarga de operadores en C++: Guía práctica con ejemplos de código
- Structs en C++ explicados con un ejemplo práctico
- Dominar std::list en C++: Sintaxis, Funciones y Ejemplos Prácticos
- Funciones en C++ Explicadas con Ejemplos de Código Prácticos
- Struct vs Class en C++: Diferencias Clave, Sintaxis y Mejores Casos de Uso
- calloc() en C: Asignación de memoria inicializada a cero y ejemplo práctico
- Dominando la función free() en C: Guía práctica y ejemplo