Dominando las funciones virtuales en C++: conceptos, ejemplos y la palabra clave override
Funciones virtuales en C++
Este tutorial explica las funciones virtuales en C++, su propósito y uso práctico ilustrado con ejemplos de código.
Una función virtual es un miembro de la clase base que esperamos que las clases derivadas sobreescriban. Declarar una función como virtual asegura que la implementación más derivada sea invocada, incluso cuando se accede a través de un puntero o referencia a la clase base.
Cuando una clase base contiene un miembro no virtual, llamar a ese miembro a través de un puntero base siempre ejecutará la versión base, independientemente del tipo de objeto real. Considere lo siguiente:
class Base {
public:
void imprimir() {
// implementación base
}
};
class Derivada : public Base {
public:
void imprimir() {
// implementación derivada
}
};
En el programa siguiente, un Base* apunta a una instancia de Derivada, sin embargo imprimir() resuelve a la versión base:
int main() {
Derivada derivada1;
Base* base1 = &derivada1;
base1->imprimir(); // llama a Base::imprimir()
return 0;
}
Para lograr comportamiento polimórfico, declare la función como virtual en la clase base:
class Base {
public:
virtual void imprimir() {
// implementación base
}
};
Las funciones virtuales son centrales para el polimorfismo en C++. Para una inmersión más profunda, consulte nuestra guía de Polimorfismo en C++.
Ejemplo 1: Uso básico de función virtual
#include <iostream>
using namespace std;
class Base {
public:
virtual void imprimir() {
cout << "Función Base" << endl;
}
};
class Derivada : public Base {
public:
void imprimir() override {
cout << "Función Derivada" << endl;
}
};
int main() {
Derivada derivada1;
Base* base1 = &derivada1;
base1->imprimir(); // imprime Función Derivada
return 0;
}
Salida
Función Derivada
Como Base::imprimir es virtual, la llamada despacha a Derivada::imprimir incluso a través de un puntero base.

El identificador override en C++
Desde C++11, la palabra clave override ayuda a prevenir sobreescrituras accidentales al forzar al compilador a verificar que una función realmente sobreescribe un miembro virtual de la base.
Ejemplo:
class Base {
public:
virtual void imprimir() {
// implementación base
}
};
class Derivada : public Base {
public:
void imprimir() override {
// implementación derivada
}
};
Si define la función de sobreescritura fuera de la clase, use:
class Derivada : public Base {
public:
void imprimir() override; // declaración
};
void Derivada::imprimir() {
// implementación
}
Usando override de forma segura
Sin override, un error tipográfico o discrepancia en la firma resulta silenciosamente en una nueva función en lugar de una sobreescritura, llevando a errores sutiles. El compilador emitirá un error, haciendo el problema inmediatamente visible. Errores comunes incluyen:
- Nombres mal escritos: ej.
imprimir()en lugar deimprimir() - Tipos de retorno diferentes: ej. devolver
inten lugar devoid - Discrepancia de parámetros: listas de parámetros diferentes
- Falta virtual en base: la función base no es virtual en absoluto
Uso práctico de funciones virtuales
Considere una clase base Animal con clases derivadas Perro y Gato. Cada clase tiene un miembro tipo inicializado por su constructor.
class Animal {
private:
string tipo;
public:
Animal() : tipo("Animal") {}
virtual string getTipo() const {
return tipo;
}
};
class Perro : public Animal {
private:
string tipo;
public:
Perro() : tipo("Perro") {}
string getTipo() const override {
return tipo;
}
};
class Gato : public Animal {
private:
string tipo;
public:
Gato() : tipo("Gato") {}
string getTipo() const override {
return tipo;
}
};
Con getTipo() virtual, un solo helper puede imprimir cualquier animal:
void imprimir(Animal* ani) {
cout << "Animal: " << ani->getTipo() << endl;
}
Uso en main():
int main() {
Animal* animal1 = new Animal();
Animal* perro1 = new Perro();
Animal* gato1 = new Gato();
imprimir(animal1);
imprimir(perro1);
imprimir(gato1);
delete animal1;
delete perro1;
delete gato1;
return 0;
}
Salida
Animal: Animal Animal: Perro Animal: Gato
Al hacer getTipo() virtual, evitamos duplicar la lógica de imprimir() en cada clase derivada y aseguramos que se acceda al tipo correcto.
Lenguaje C
- Cómo Pasar Arrays a Funciones en C++: Guía Práctica
- Clases y Objetos en C++: Guía Práctica de Programación Orientada a Objetos
- Funciones y clases amigas en C++: dominando el control de acceso
- Dominando las plantillas de clase en C++: guía práctica
- Funciones en C++ Explicadas con Ejemplos de Código Prácticos
- Dominando funciones en C: Estructura, declaración y mejores prácticas
- Entendiendo las Clases de Almacenamiento en C++: Alcance, Vida y Uso
- Dominando la Sobrecarga en C++: Funciones y Operadores Explicados
- Entendiendo el Polimorfismo en C++: Guía Práctica
- Dominando la Abstracción de Datos en C++: Simplifica el Diseño con Separación Interfaz-Implementación
- Dominar las funciones en Verilog para diseño RTL eficiente