Menu
Coddy logo textTech

Funciones virtuales puras

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy. Lección 59 de 104.

Una función virtual pura es una función virtual que no tiene implementación en la clase base. Se declara asignando = 0 a la declaración de la función. Esto le indica al compilador que las clases derivadas deben proporcionar su propia implementación.

class Shape {
public:
    virtual double area() = 0;  // Función virtual pura
    virtual ~Shape() = default;
};

A diferencia de las funciones virtuales normales, que proporcionan un comportamiento predeterminado, las funciones virtuales puras definen un contrato: cualquier clase derivada concreta debe implementar esta función para poder ser instanciada. La clase base simplemente declara qué debe hacerse, no cómo.

class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}
    
    double area() override {
        return 3.14159 * radius * radius;
    }
};

class Rectangle : public Shape {
    double width, height;
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    
    double area() override {
        return width * height;
    }
};

Tanto Circle como Rectangle deben implementar area() porque es una función virtual pura en Shape. Si una clase derivada no implementa todas las funciones virtuales puras, también se vuelve abstracta y no se puede instanciar.

Las funciones virtuales puras son esenciales cuando la clase base no puede proporcionar una implementación predeterminada significativa. Toda figura tiene un área, pero no hay una forma sensata de calcular el «área de una figura genérica» sin conocer el tipo específico de figura.

challenge icon

Desafío

Fácil

Construyamos un sistema de procesamiento de pagos que demuestre cómo las funciones virtuales puras hacen cumplir un contrato entre distintos métodos de pago. Crearás una clase base abstracta que define lo que todo procesador de pagos debe hacer y, después, implementarás tipos de pago concretos que cumplan ese contrato.

Organizarás tu código en tres archivos:

  • PaymentProcessor.h: Define una clase abstracta PaymentProcessor que sirve como modelo para todos los métodos de pago. Esta clase debe tener:
    • Un miembro protegido std::string accountId
    • Un constructor que inicialice el ID de la cuenta
    • Un método virtual puro processPayment(double amount): cada tipo de pago debe implementarlo de forma diferente
    • Un método virtual puro getProcessorName() que devuelva un std::string
    • Un destructor virtual
  • PaymentMethods.h: Implementa dos procesadores de pagos concretos que hereden de PaymentProcessor:

    CreditCardProcessor:

    • Un miembro privado double feePercentage (la tasa de comisión de la transacción)
    • Un constructor que reciba el ID de la cuenta y el porcentaje de comisión
    • Implementa processPayment() para calcular la comisión (amount * feePercentage / 100) y, después, imprimir:
      Credit Card [<accountId>]: Charged $<amount> (Fee: $<fee>)
    • Implementa getProcessorName() para devolver "CreditCard"

    BankTransferProcessor:

    • Un miembro privado std::string bankName
    • Un constructor que reciba el ID de la cuenta y el nombre del banco
    • Implementa processPayment() para imprimir:
      Bank Transfer [<accountId>] via <bankName>: Transferred $<amount>
    • Implementa getProcessorName() para devolver "BankTransfer"
  • main.cpp: Lee cuatro entradas (cada una en una línea separada):
    1. ID de la cuenta de la tarjeta de crédito
    2. Porcentaje de comisión de la tarjeta de crédito (double)
    3. ID de la cuenta bancaria
    4. Nombre del banco

    Crea ambos procesadores de pagos y guárdalos en un arreglo de punteros PaymentProcessor*. Procesa un pago de 100.0 mediante cada procesador y muestra el nombre del procesador antes de cada transacción:

    Processing with <processorName>:
    <processPayment output>

    Imprime una línea en blanco entre los procesadores. Libera los objetos asignados dinámicamente cuando hayas terminado.

Por ejemplo, con las entradas CC-4521, 2.5, BA-7890 y National Bank:

Processing with CreditCard:
Credit Card [CC-4521]: Charged $100 (Fee: $2.5)

Processing with BankTransfer:
Bank Transfer [BA-7890] via National Bank: Transferred $100

Como PaymentProcessor tiene funciones virtuales puras, no puedes crear una instancia directamente: solo se pueden crear las implementaciones concretas que cumplan el contrato. Esto garantiza que cada método de pago proporcione su propia lógica de procesamiento específica.

Pruébalo tú mismo

#include <iostream>
#include <string>
#include "PaymentMethods.h"

using namespace std;

int main() {
    // Leer entradas
    string ccAccountId;
    double feePercentage;
    string bankAccountId;
    string bankName;
    
    getline(cin, ccAccountId);
    cin >> feePercentage;
    cin.ignore();
    getline(cin, bankAccountId);
    getline(cin, bankName);
    
    // TODO: Crea un array de punteros PaymentProcessor* con 2 elementos
    
    // TODO: Crea objetos CreditCardProcessor y BankTransferProcessor
    // y almacénalos en el array
    
    // TODO: Recorre el array y para cada procesador:
    // 1. Print "Processing with <processorName>:"
    // 2. Llama a processPayment con amount 100.0
    // 3. Imprime una línea en blanco entre procesadores (no después del último)
    
    // TODO: Limpia los objetos asignados dinámicamente
    
    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de C++ online