Lección 4 · 4 de 20
Clases abstractas y polimorfismo · C++
Defina una interfaz con funciones virtuales puras en C++ y recorra objetos distintos con punteros a la base: métodos de pago y sus comisiones.

PaymentMethod es una clase abstracta: name() y fee() son virtuales puras (terminan en = 0) y no tienen cuerpo. No se puede crear un objeto suyo (PaymentMethod p; no compila); sirve de interfaz, un contrato que cumplen las clases derivadas. Se usa struct, que es lo mismo que class pero con todo público por defecto.
Aun así, la clase abstracta puede tener código común: total() suma el importe y la comisión llamando a fee(), sin saber de qué método de pago se trata. Card, PayPal y Transfer solo dicen su nombre y cómo calculan la comisión. Transfer cobra una tarifa fija y no usa el importe, por eso su parámetro no tiene nombre.
El polimorfismo aparece con los punteros a la base: methods es un std::vector<const PaymentMethod*> con las direcciones de tres objetos de tipos distintos (&card, &paypal, &transfer). Con m->name() y m->total(amount) (la flecha -> llama a un método a través de un puntero), cada objeto ejecuta su propia versión.
El bucle busca la opción más barata para cada importe guardando un puntero a la mejor: con 20 euros gana la tarjeta (0,30 de comisión) y con 80, la transferencia (1 euro fijo frente a 1,20). Para añadir otro método de pago bastaría con otra clase derivada: el bucle no cambia.
Aquí los objetos viven en main y los punteros solo los señalan. Cuando se crean con new y se borran a través de la base, el destructor virtual es imprescindible; en la lección 7 verá cómo los punteros inteligentes se encargan de liberarlos.
Código · main.cpp
#include <iostream>#include <vector>struct PaymentMethod { // abstract: pure virtual functions (= 0), no objects of it virtual ~PaymentMethod() = default; virtual const char* name() const = 0; virtual double fee(double amount) const = 0; double total(double amount) const { return amount + fee(amount); }};struct Card : PaymentMethod { const char* name() const override { return "Card"; } double fee(double amount) const override { return amount * 0.015; }};struct PayPal : PaymentMethod { const char* name() const override { return "PayPal"; } double fee(double amount) const override { return 0.35 + amount * 0.029; }};struct Transfer : PaymentMethod { const char* name() const override { return "Transfer"; } double fee(double) const override { return 1.0; } // flat fee};int main() { Card card; PayPal paypal; Transfer transfer; std::vector<const PaymentMethod*> methods = {&card, &paypal, &transfer}; // pointers to the base for (double amount : {20.0, 80.0}) { const PaymentMethod* best = methods[0]; for (const PaymentMethod* m : methods) { std::cout << amount << " EUR by " << m->name() << ": " << m->total(amount) << "\n"; if (m->fee(amount) < best->fee(amount)) best = m; } std::cout << " cheapest: " << best->name() << "\n"; }}Salida · c++ -std=c++17 main.cpp -o main && ./main
20 EUR by Card: 20.3 20 EUR by PayPal: 20.93 20 EUR by Transfer: 21 cheapest: Card 80 EUR by Card: 81.2 80 EUR by PayPal: 82.67 80 EUR by Transfer: 81 cheapest: Transfer
Para recordar
- Una función virtual pura (
= 0) no tiene cuerpo y obliga a las derivadas a darle uno. - Una clase con alguna virtual pura es abstracta: no se pueden crear objetos suyos.
- Un puntero a la base puede señalar a cualquier derivada.
p->metodo()ejecuta la versión del objeto real.- La base de una jerarquía polimórfica lleva un destructor virtual.
Ejercicio
Añada un método de pago Cash sin comisión cuyo nombre sea "Cash", métalo en methods y compruebe que pasa a ser el más barato con los dos importes.